blob: 6ff4c57ad1155a8cd30f28f3304ac7aa5f49c967 [file] [log] [blame]
Marcin Bukat28d54c62010-04-26 21:40:16 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id:$
9 *
10 * Copyright (C) 2010 Marcin Bukat
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include "config.h"
22
23#include <stdlib.h>
24#include <stdio.h>
25#include "inttypes.h"
26#include "string.h"
27#include "cpu.h"
28#include "system.h"
29#include "lcd.h"
30#include "kernel.h"
31#include "thread.h"
32#include "storage.h"
33#include "usb.h"
34#include "disk.h"
35#include "font.h"
36#include "adc.h"
37#include "backlight.h"
38#include "backlight-target.h"
39#include "button.h"
40#include "panic.h"
41#include "power.h"
42#include "powermgmt.h"
43#include "file.h"
44
45#include "common.h"
Rafaël Carré5d236b22010-05-27 09:41:46 +000046#include "version.h"
Marcin Bukat28d54c62010-04-26 21:40:16 +000047
48#include <stdarg.h>
49
50/* Maximum allowed firmware image size. 10MB is more than enough */
51#define MAX_LOADSIZE (10*1024*1024)
52
53#define DRAM_START 0x31000000
54
55#define BOOTMENU_TIMEOUT (10*HZ)
56#define BOOTMENU_OPTIONS 3
57
Marcin Bukat69fa42d2010-05-04 11:16:17 +000058#define EVENT_NONE 0x00
59#define EVENT_ON 0x01
60#define EVENT_AC 0x02
61#define EVENT_USB 0x04
62
Marcin Bukat28d54c62010-04-26 21:40:16 +000063/* From common.c */
64extern int line;
65static const char *bootmenu_options[] = {
66 "Boot rockbox",
67 "Boot MPIO firmware",
68 "Shutdown"
69};
70
71enum option_t {
72 rockbox,
73 mpio_firmware,
74 shutdown
75};
76
77int usb_screen(void)
78{
79 return 0;
80}
81
Marcin Bukat69fa42d2010-05-04 11:16:17 +000082static inline bool _charger_inserted(void)
Marcin Bukat28d54c62010-04-26 21:40:16 +000083{
84 return (GPIO1_READ & (1<<14)) ? false : true;
85}
86
Marcin Bukat69fa42d2010-05-04 11:16:17 +000087static inline bool _battery_full(void)
Marcin Bukat28d54c62010-04-26 21:40:16 +000088{
89 return (GPIO_READ & (1<<30)) ? true : false;
90}
91
92/* Reset the cookie for the crt0 crash check */
Marcin Bukat69fa42d2010-05-04 11:16:17 +000093static inline void __reset_cookie(void)
Marcin Bukat28d54c62010-04-26 21:40:16 +000094{
95 asm(" move.l #0,%d0");
96 asm(" move.l %d0,0x10017ffc");
97}
98
Marcin Bukat69fa42d2010-05-04 11:16:17 +000099static void start_rockbox(void)
Marcin Bukat28d54c62010-04-26 21:40:16 +0000100{
101 adc_close();
102 asm(" move.w #0x2700,%sr");
103 __reset_cookie();
104 asm(" move.l %0,%%d0" :: "i"(DRAM_START));
105 asm(" movec.l %d0,%vbr");
106 asm(" move.l %0,%%sp" :: "m"(*(int *)DRAM_START));
107 asm(" move.l %0,%%a0" :: "m"(*(int *)(DRAM_START+4)));
108 asm(" jmp (%a0)");
109}
110
Marcin Bukat69fa42d2010-05-04 11:16:17 +0000111static void start_mpio_firmware(void)
Marcin Bukat28d54c62010-04-26 21:40:16 +0000112{
113 asm(" move.w #0x2700,%sr");
114 __reset_cookie();
115 asm(" movec.l %d0,%vbr");
116 asm(" move.l 0,%sp");
117 asm(" jmp 8");
118}
119
Marcin Bukat69fa42d2010-05-04 11:16:17 +0000120static void __reset(void)
Marcin Bukat28d54c62010-04-26 21:40:16 +0000121{
122 asm(" move.w #0x2700,%sr");
123 __reset_cookie();
124 asm(" movec.l %d0,%vbr");
125 asm(" move.l (0), %sp");
126 asm(" movea.l (4),%a0");
127 asm(" jmp (%a0)");
128}
129
Marcin Bukat69fa42d2010-05-04 11:16:17 +0000130static void __shutdown(void)
Marcin Bukat28d54c62010-04-26 21:40:16 +0000131{
Marcin Bukat78d54fa2010-07-09 09:12:51 +0000132 if (_charger_inserted())
133 /* if AC power do nothing */
134 return;
135
Marcin Bukat28d54c62010-04-26 21:40:16 +0000136 /* We need to gracefully spin down the disk to prevent clicks. */
137 if (ide_powered())
138 {
139 /* Make sure ATA has been initialized. */
140 storage_init();
141
142 /* And put the disk into sleep immediately. */
143 storage_sleepnow();
144 }
145
146 /* Backlight OFF */
147 _backlight_off();
148 __reset_cookie();
149
Marcin Bukat28d54c62010-04-26 21:40:16 +0000150 power_off();
Marcin Bukat28d54c62010-04-26 21:40:16 +0000151}
152
153/* Print the battery voltage (and a warning message). */
Marcin Bukat69fa42d2010-05-04 11:16:17 +0000154static void check_battery(void)
Marcin Bukat28d54c62010-04-26 21:40:16 +0000155{
156
157 int battery_voltage, batt_int, batt_frac;
158
159 battery_voltage = battery_adc_voltage();
160 batt_int = battery_voltage / 1000;
161 batt_frac = (battery_voltage % 1000) / 10;
162
163 printf("Battery: %d.%02dV", batt_int, batt_frac);
164
165 if (battery_voltage <= 3500)
166 {
167 printf("WARNING! BATTERY LOW!!");
168 sleep(HZ*2);
169 }
170
171}
172
173
Marcin Bukat69fa42d2010-05-04 11:16:17 +0000174static void lcd_putstring_centered(const char *string)
Marcin Bukat28d54c62010-04-26 21:40:16 +0000175{
176 int w,h;
177 font_getstringsize(string, &w, &h, FONT_SYSFIXED);
178 lcd_putsxy((LCD_WIDTH-w)/2, (LCD_HEIGHT-h)/2, string);
179}
180
Marcin Bukat69fa42d2010-05-04 11:16:17 +0000181static void rb_boot(void)
Marcin Bukat28d54c62010-04-26 21:40:16 +0000182{
183 int rc;
Marcin Bukat69fa42d2010-05-04 11:16:17 +0000184
Marcin Bukat57484bd2010-07-06 17:30:10 +0000185 /* boost to speedup rb image loading */
186 cpu_boost(true);
187
Marcin Bukat69fa42d2010-05-04 11:16:17 +0000188 rc = storage_init();
189 if(rc)
190 {
191 printf("ATA error: %d", rc);
192 sleep(HZ*5);
193 return;
194 }
195
196 disk_init();
197
198 rc = disk_mount_all();
199 if (rc<=0)
200 {
201 printf("No partition found");
202 sleep(HZ*5);
203 return;
204 }
205
206 printf("Loading firmware");
207
208 rc = load_firmware((unsigned char *)DRAM_START,
209 BOOTFILE, MAX_LOADSIZE);
210
211 if (rc < EOK)
212 {
213 printf("Error!");
214 printf("Can't load " BOOTFILE ": ");
215 printf("Result: %s", strerror(rc));
216 sleep(HZ*5);
217 return;
218 }
219
Marcin Bukat57484bd2010-07-06 17:30:10 +0000220 cpu_boost(false);
Marcin Bukat69fa42d2010-05-04 11:16:17 +0000221 start_rockbox();
222}
223
224static void bootmenu(void)
225{
Marcin Bukat28d54c62010-04-26 21:40:16 +0000226 enum option_t i;
227 enum option_t option = rockbox;
228 int button;
229 const char select[] = "->";
230 long start_tick = current_tick;
231
232 /* backbone of menu */
233 /* run the loader */
234 printf("Rockbox boot loader");
Rafaël Carré5d236b22010-05-27 09:41:46 +0000235 printf("Ver: " RBVERSION);
Marcin Bukat28d54c62010-04-26 21:40:16 +0000236
237 check_battery();
238
239 printf("");
240 printf("=========================");
241
242 line += BOOTMENU_OPTIONS+2; /* skip lines */
243
244 printf("=========================");
245 printf("");
246 printf(" [FF] [PREV] to move ");
247 printf(" [PLAY] to confirm ");
248
249 /* content of menu and keys handling */
250 while (TIME_BEFORE(current_tick,start_tick + BOOTMENU_TIMEOUT))
251 {
252 /* Draw the menu. */
253 line = 6; /* move below header */
254
255 for (i=0;i<BOOTMENU_OPTIONS;i++)
256 {
257 if (i != option)
258 printf(" %s",bootmenu_options[i]);
259 else
260 printf("%s %s",select,bootmenu_options[i]);
261 }
262
263 line = 15;
264
265 printf("Time left: %ds",(BOOTMENU_TIMEOUT -
266 (current_tick - start_tick))/HZ);
267
268 lcd_update();
269
Marcin Bukat78d54fa2010-07-09 09:12:51 +0000270 button = BUTTON_NONE;
Marcin Bukat28d54c62010-04-26 21:40:16 +0000271 button = button_get_w_tmo(HZ);
272
273 switch (button)
274 {
275 case BUTTON_PREV:
Marcin Bukat57484bd2010-07-06 17:30:10 +0000276 case BUTTON_RC_PREV:
Marcin Bukat28d54c62010-04-26 21:40:16 +0000277 if (option > rockbox)
278 option--;
279 else
280 option = shutdown;
281 break;
282
283 case BUTTON_NEXT:
Marcin Bukat57484bd2010-07-06 17:30:10 +0000284 case BUTTON_RC_NEXT:
Marcin Bukat28d54c62010-04-26 21:40:16 +0000285 if (option < shutdown)
286 option++;
287 else
288 option = rockbox;
289 break;
290
291 case BUTTON_PLAY:
Marcin Bukat57484bd2010-07-06 17:30:10 +0000292 case BUTTON_RC_PLAY:
Marcin Bukat28d54c62010-04-26 21:40:16 +0000293 case (BUTTON_PLAY|BUTTON_REC):
294 reset_screen();
295
296 switch (option)
297 {
298 case rockbox:
Marcin Bukat69fa42d2010-05-04 11:16:17 +0000299 rb_boot();
Marcin Bukat28d54c62010-04-26 21:40:16 +0000300 break;
301
302 case mpio_firmware:
303 start_mpio_firmware();
304 break;
305
306 default:
Marcin Bukat69fa42d2010-05-04 11:16:17 +0000307 return;
Marcin Bukat28d54c62010-04-26 21:40:16 +0000308 break;
309 }
310 }
311}
312/* timeout */
Marcin Bukat28d54c62010-04-26 21:40:16 +0000313}
314
315void main(void)
316{
317 /* messages */
318 const char usb_connect_msg[] = "Bootloader USB mode";
319 const char charging_msg[] = "Charging...";
320 const char complete_msg[] = "Charging complete";
Marcin Bukat28d54c62010-04-26 21:40:16 +0000321
Marcin Bukat69fa42d2010-05-04 11:16:17 +0000322 /* helper variable for messages */
Marcin Bukat28d54c62010-04-26 21:40:16 +0000323 bool blink_toggle = false;
Marcin Bukat28d54c62010-04-26 21:40:16 +0000324
Marcin Bukat28d54c62010-04-26 21:40:16 +0000325 int button;
Marcin Bukat69fa42d2010-05-04 11:16:17 +0000326 unsigned int event = EVENT_NONE;
327 unsigned int last_event = EVENT_NONE;
Marcin Bukat28d54c62010-04-26 21:40:16 +0000328
Marcin Bukat57484bd2010-07-06 17:30:10 +0000329 /* this is default mode after power_init() */
330 bool high_current_charging = true;
331
Marcin Bukat28d54c62010-04-26 21:40:16 +0000332 power_init();
333
334 system_init();
335 kernel_init();
336
337 set_cpu_frequency(CPUFREQ_NORMAL);
338 coldfire_set_pllcr_audio_bits(DEFAULT_PLLCR_AUDIO_BITS);
339
340 enable_irq();
341 lcd_init();
342
Marcin Bukat69fa42d2010-05-04 11:16:17 +0000343 /* only lowlevel functions no queue init */
344 _backlight_init();
345 _backlight_hw_on();
346
347 /* setup font system*/
Marcin Bukat28d54c62010-04-26 21:40:16 +0000348 font_init();
349 lcd_setfont(FONT_SYSFIXED);
Marcin Bukat69fa42d2010-05-04 11:16:17 +0000350
351 /* buttons reading */
Marcin Bukat28d54c62010-04-26 21:40:16 +0000352 adc_init();
353 button_init();
Marcin Bukat69fa42d2010-05-04 11:16:17 +0000354
Marcin Bukat28d54c62010-04-26 21:40:16 +0000355 usb_init();
Marcin Bukat69fa42d2010-05-04 11:16:17 +0000356 cpu_idle_mode(true);
Marcin Bukat28d54c62010-04-26 21:40:16 +0000357
Marcin Bukat69fa42d2010-05-04 11:16:17 +0000358 /* Handle wakeup event. Possibilities are:
359 * ON button (PLAY)
360 * USB insert
361 * AC charger plug
362 */
363
364 while(1)
Marcin Bukat28d54c62010-04-26 21:40:16 +0000365 {
Marcin Bukat69fa42d2010-05-04 11:16:17 +0000366 /* read buttons */
367 event = EVENT_NONE;
368 button = button_get_w_tmo(HZ);
Marcin Bukat28d54c62010-04-26 21:40:16 +0000369
Marcin Bukat57484bd2010-07-06 17:30:10 +0000370 if ( (button & BUTTON_PLAY) || (button & BUTTON_RC_PLAY) )
Marcin Bukat69fa42d2010-05-04 11:16:17 +0000371 event |= EVENT_ON;
372
373 if ( usb_detect() == USB_INSERTED )
374 event |= EVENT_USB;
Marcin Bukat28d54c62010-04-26 21:40:16 +0000375
Marcin Bukat69fa42d2010-05-04 11:16:17 +0000376 if ( _charger_inserted() )
377 event |= EVENT_AC;
378
379 reset_screen();
380 switch (event)
Marcin Bukat28d54c62010-04-26 21:40:16 +0000381 {
Marcin Bukat69fa42d2010-05-04 11:16:17 +0000382 case EVENT_ON:
383 case (EVENT_ON | EVENT_AC):
384 /* hold is handled in button driver */
385 cpu_idle_mode(false);
Marcin Bukatcd879712010-06-14 10:42:45 +0000386 ide_power_enable(true);
Marcin Bukat28d54c62010-04-26 21:40:16 +0000387
Marcin Bukat69fa42d2010-05-04 11:16:17 +0000388 if (button == (BUTTON_PLAY|BUTTON_REC))
389 bootmenu();
390 else
391 rb_boot();
392
Marcin Bukat28d54c62010-04-26 21:40:16 +0000393 break;
394
Marcin Bukat69fa42d2010-05-04 11:16:17 +0000395 case EVENT_AC:
Marcin Bukat78d54fa2010-07-09 09:12:51 +0000396 /* AC plug in */
Marcin Bukat69fa42d2010-05-04 11:16:17 +0000397 if (!(last_event & EVENT_AC))
Marcin Bukatcd879712010-06-14 10:42:45 +0000398 {
Marcin Bukat57484bd2010-07-06 17:30:10 +0000399 /* reset charging circuit */
Marcin Bukatcd879712010-06-14 10:42:45 +0000400 and_l(~(1<<23), &GPIO_ENABLE);
401 }
Marcin Bukat28d54c62010-04-26 21:40:16 +0000402
Marcin Bukat69fa42d2010-05-04 11:16:17 +0000403 /* USB unplug */
404 if (last_event & EVENT_USB)
Marcin Bukatcd879712010-06-14 10:42:45 +0000405 {
Marcin Bukat69fa42d2010-05-04 11:16:17 +0000406 usb_enable(false);
Marcin Bukat78d54fa2010-07-09 09:12:51 +0000407 sleep(HZ);
Marcin Bukatcd879712010-06-14 10:42:45 +0000408 ide_power_enable(false);
Marcin Bukat78d54fa2010-07-09 09:12:51 +0000409 sleep(HZ);
Marcin Bukatcd879712010-06-14 10:42:45 +0000410 }
Marcin Bukat69fa42d2010-05-04 11:16:17 +0000411
Marcin Bukat28d54c62010-04-26 21:40:16 +0000412 if(!_battery_full())
413 {
Marcin Bukat69fa42d2010-05-04 11:16:17 +0000414 if (blink_toggle)
415 lcd_putstring_centered(charging_msg);
416
Marcin Bukat28d54c62010-04-26 21:40:16 +0000417 blink_toggle = !blink_toggle;
Marcin Bukat28d54c62010-04-26 21:40:16 +0000418 }
Marcin Bukat57484bd2010-07-06 17:30:10 +0000419 else /* end of charge condition */
Marcin Bukat28d54c62010-04-26 21:40:16 +0000420 {
Marcin Bukat57484bd2010-07-06 17:30:10 +0000421 /* put LTC1733 into shutdown mode */
422 or_l((1<<23), &GPIO_ENABLE);
423
424 if (high_current_charging)
425 {
426 /* switch to low current mode */
427 and_l(~(1<<15), &GPIO_OUT);
428
429 /* reset charging circuit */
430 and_l(~(1<<23), &GPIO_ENABLE);
431
432 high_current_charging = false;
433 }
434 else
435 {
436 lcd_putstring_centered(complete_msg);
437 }
Marcin Bukat28d54c62010-04-26 21:40:16 +0000438 }
Marcin Bukat28d54c62010-04-26 21:40:16 +0000439 check_battery();
440 break;
Marcin Bukat28d54c62010-04-26 21:40:16 +0000441
Marcin Bukat69fa42d2010-05-04 11:16:17 +0000442 case EVENT_USB:
443 case (EVENT_USB | EVENT_AC):
Marcin Bukat78d54fa2010-07-09 09:12:51 +0000444 /* AC plug in while in USB mode */
Marcin Bukat69fa42d2010-05-04 11:16:17 +0000445 if (!(last_event & EVENT_AC))
Marcin Bukatcd879712010-06-14 10:42:45 +0000446 {
Marcin Bukat57484bd2010-07-06 17:30:10 +0000447 /* reset charger circuit */
Marcin Bukatcd879712010-06-14 10:42:45 +0000448 and_l(~(1<<23), &GPIO_ENABLE);
449 }
Marcin Bukat69fa42d2010-05-04 11:16:17 +0000450
Marcin Bukat78d54fa2010-07-09 09:12:51 +0000451 /* USB plug in */
Marcin Bukat69fa42d2010-05-04 11:16:17 +0000452 if (!(last_event & EVENT_USB))
453 {
454 /* init USB */
455 ide_power_enable(true);
456 sleep(HZ/20);
457 usb_enable(true);
458 }
Marcin Bukat78d54fa2010-07-09 09:12:51 +0000459
460 /* display blinking USB indicator */
Marcin Bukat69fa42d2010-05-04 11:16:17 +0000461 line = 0;
462
463 if (blink_toggle)
464 lcd_putstring_centered(usb_connect_msg);
465
466 check_battery();
467 blink_toggle = !blink_toggle;
468 storage_spin();
469 break;
470
471 default:
Marcin Bukat78d54fa2010-07-09 09:12:51 +0000472 /* USB unplug */
Marcin Bukat57484bd2010-07-06 17:30:10 +0000473 if (last_event & EVENT_USB)
474 {
Marcin Bukat78d54fa2010-07-09 09:12:51 +0000475 /* disable USB */
Marcin Bukat57484bd2010-07-06 17:30:10 +0000476 usb_enable(false);
Marcin Bukat78d54fa2010-07-09 09:12:51 +0000477 sleep(HZ);
Marcin Bukat57484bd2010-07-06 17:30:10 +0000478 ide_power_enable(false);
Marcin Bukat78d54fa2010-07-09 09:12:51 +0000479 sleep(HZ);
Marcin Bukat57484bd2010-07-06 17:30:10 +0000480 }
481
Marcin Bukat78d54fa2010-07-09 09:12:51 +0000482 /* spurious wakeup ?*/
Marcin Bukat69fa42d2010-05-04 11:16:17 +0000483 __shutdown();
484 break;
Marcin Bukat28d54c62010-04-26 21:40:16 +0000485 }
Marcin Bukat28d54c62010-04-26 21:40:16 +0000486 lcd_update();
Marcin Bukat69fa42d2010-05-04 11:16:17 +0000487 last_event = event;
Marcin Bukat28d54c62010-04-26 21:40:16 +0000488 }
489
Marcin Bukat28d54c62010-04-26 21:40:16 +0000490}
491
492/* These functions are present in the firmware library, but we reimplement
493 them here because the originals do a lot more than we want */
494void screen_dump(void)
495{
496}