summaryrefslogtreecommitdiff
path: root/bootloader/ipod6g.c
diff options
context:
space:
mode:
Diffstat (limited to 'bootloader/ipod6g.c')
-rw-r--r--bootloader/ipod6g.c462
1 files changed, 462 insertions, 0 deletions
diff --git a/bootloader/ipod6g.c b/bootloader/ipod6g.c
new file mode 100644
index 0000000000..0ab9444578
--- /dev/null
+++ b/bootloader/ipod6g.c
@@ -0,0 +1,462 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 by Dave Chapman
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 <stdlib.h>
22#include <stdio.h>
23#include <stdarg.h>
24#include <string.h>
25
26#include "config.h"
27
28#include "inttypes.h"
29#include "cpu.h"
30#include "system.h"
31#include "lcd.h"
32#include "../kernel-internal.h"
33#include "file_internal.h"
34#include "storage.h"
35#include "fat.h"
36#include "disk.h"
37#include "font.h"
38#include "backlight.h"
39#include "backlight-target.h"
40#include "button.h"
41#include "panic.h"
42#include "power.h"
43#include "file.h"
44#include "common.h"
45#include "rb-loader.h"
46#include "loader_strerror.h"
47#include "version.h"
48#include "powermgmt.h"
49#include "usb.h"
50#ifdef HAVE_SERIAL
51#include "serial.h"
52#endif
53
54#include "s5l8702.h"
55#include "clocking-s5l8702.h"
56#include "spi-s5l8702.h"
57#include "i2c-s5l8702.h"
58#include "gpio-s5l8702.h"
59#include "pmu-target.h"
60#include "nor-target.h"
61
62
63#define FW_ROCKBOX 0
64#define FW_APPLE 1
65
66#define ERR_RB 0
67#define ERR_OF 1
68#define ERR_HDD 2
69
70/* Safety measure - maximum allowed firmware image size.
71 The largest known current (October 2009) firmware is about 6.2MB so
72 we set this to 8MB.
73*/
74#define MAX_LOADSIZE (8*1024*1024)
75
76#define LCD_RBYELLOW LCD_RGBPACK(255,192,0)
77#define LCD_REDORANGE LCD_RGBPACK(255,70,0)
78
79extern void bss_init(void);
80extern uint32_t _movestart;
81extern uint32_t start_loc;
82
83extern int line;
84
85#ifdef HAVE_BOOTLOADER_USB_MODE
86static void usb_mode(void)
87{
88 int button;
89
90 verbose = true;
91
92 printf("Entering USB mode...");
93
94 powermgmt_init();
95
96 /* The code will ask for the maximum possible value */
97 usb_charging_enable(USB_CHARGING_ENABLE);
98
99 usb_init();
100 usb_start_monitoring();
101
102 /* Wait until USB is plugged */
103 while (usb_detect() != USB_INSERTED)
104 {
105 printf("Plug USB cable");
106 line--;
107 sleep(HZ/10);
108 }
109
110 while(1)
111 {
112 button = button_get_w_tmo(HZ/10);
113
114 if (button == SYS_USB_CONNECTED)
115 break; /* Hit */
116
117 if (usb_detect() == USB_EXTRACTED)
118 break; /* Cable pulled */
119
120 /* Wait for threads to connect or cable is pulled */
121 printf("USB: Connecting...");
122 line--;
123 }
124
125 if (button == SYS_USB_CONNECTED)
126 {
127 /* Got the message - wait for disconnect */
128 printf("Bootloader USB mode");
129
130 /* Ack the SYS_USB_CONNECTED polled from the button queue */
131 usb_acknowledge(SYS_USB_CONNECTED_ACK);
132
133 while(1)
134 {
135 button = button_get_w_tmo(HZ/2);
136 if (button == SYS_USB_DISCONNECTED)
137 break;
138 }
139 }
140
141 /* We don't want the HDD to spin up if the USB is attached again */
142 usb_close();
143 printf("USB mode exit ");
144}
145#endif /* HAVE_BOOTLOADER_USB_MODE */
146
147void fatal_error(int err)
148{
149 verbose = true;
150
151 /* System font is 6 pixels wide */
152 line++;
153 switch (err)
154 {
155 case ERR_RB:
156#ifdef HAVE_BOOTLOADER_USB_MODE
157 usb_mode();
158 printf("Hold MENU+SELECT to reboot");
159 break;
160#endif
161 case ERR_HDD:
162 printf("Hold MENU+SELECT to reboot");
163 printf("then SELECT+PLAY for disk mode");
164 break;
165 case ERR_OF:
166 printf("Hold MENU+SELECT to reboot");
167 printf("and enter Rockbox firmware");
168 break;
169 }
170
171 if (ide_powered())
172 ata_sleepnow(); /* Immediately spindown the disk. */
173
174 line++;
175 lcd_set_foreground(LCD_REDORANGE);
176 while (1) {
177 lcd_puts(0, line, button_hold() ? "Hold switch on!"
178 : " ");
179 lcd_update();
180 }
181}
182
183static void battery_trap(void)
184{
185 int vbat, old_verb;
186 int th = 50;
187
188 old_verb = verbose;
189 verbose = true;
190
191 usb_charging_maxcurrent_change(100);
192
193 while (1)
194 {
195 vbat = _battery_voltage();
196
197 /* Two reasons to use this threshold (may require adjustments):
198 * - when USB (or wall adaptor) is plugged/unplugged, Vbat readings
199 * differ as much as more than 200 mV when charge current is at
200 * maximum (~340 mA).
201 * - RB uses some sort of average/compensation for battery voltage
202 * measurements, battery icon blinks at battery_level_dangerous,
203 * when the HDD is used heavily (large database) the level drops
204 * to battery_level_shutoff quickly.
205 */
206 if (vbat >= battery_level_dangerous[0] + th)
207 break;
208 th = 200;
209
210 if (power_input_status() != POWER_INPUT_NONE) {
211 lcd_set_foreground(LCD_RBYELLOW);
212 printf("Low battery: %d mV, charging... ", vbat);
213 sleep(HZ*3);
214 }
215 else {
216 /* Wait for the user to insert a charger */
217 int tmo = 10;
218 lcd_set_foreground(LCD_REDORANGE);
219 while (1) {
220 vbat = _battery_voltage();
221 printf("Low battery: %d mV, power off in %d ", vbat, tmo);
222 if (!tmo--) {
223 /* Raise Vsysok (hyst=0.02*Vsysok) to avoid PMU
224 standby<->active looping */
225 if (vbat < 3200)
226 pmu_write(PCF5063X_REG_SVMCTL, 0xA /*3200mV*/);
227 power_off();
228 }
229 sleep(HZ*1);
230 if (power_input_status() != POWER_INPUT_NONE)
231 break;
232 line--;
233 }
234 }
235 line--;
236 }
237
238 verbose = old_verb;
239 lcd_set_foreground(LCD_WHITE);
240 printf("Battery status ok: %d mV ", vbat);
241}
242
243static int launch_onb(int clkdiv)
244{
245 /* SPI clock = PClk/(clkdiv+1) */
246 spi_clkdiv(SPI_PORT, clkdiv);
247
248 /* Actually IRAM1_ORIG contains current RB bootloader IM3 header,
249 it will be replaced by ONB IM3 header, so this function must
250 be called once!!! */
251 struct Im3Info *hinfo = (struct Im3Info*)IRAM1_ORIG;
252
253 /* Loads ONB in IRAM0, exception vector table is destroyed !!! */
254 int rc = im3_read(
255 NORBOOT_OFF + im3_nor_sz(hinfo), hinfo, (void*)IRAM0_ORIG);
256
257 if (rc != 0) {
258 /* Restore exception vector table */
259 memcpy((void*)IRAM0_ORIG, &_movestart, 4*(&start_loc-&_movestart));
260 commit_discard_idcache();
261 return rc;
262 }
263
264 /* Disable all external interrupts */
265 eint_init();
266
267 commit_discard_idcache();
268
269 /* Branch to start of IRAM */
270 asm volatile("mov pc, %0"::"r"(IRAM0_ORIG));
271 while(1);
272}
273
274/* Launch OF when kernel mode is running */
275static int kernel_launch_onb(void)
276{
277 disable_irq();
278 int rc = launch_onb(3); /* 54/4 = 13.5 MHz. */
279 enable_irq();
280 return rc;
281}
282
283static bool pmu_is_hibernated(void)
284{
285 /* OF sets GPIO3 to low when SDRAM is hibernated */
286 return !(pmu_rd(PCF5063X_REG_GPIO3CFG) & 7) &&
287 !(pmu_rd(PCF5063X_REG_OOCSHDWN) & PCF5063X_OOCSHDWN_COLDBOOT);
288}
289
290/* The boot sequence is executed on power-on or reset. After power-up
291 * the device could come from a state of hibernation, OF hibernates
292 * the iPod after an inactive period of ~30 minutes (FW 1.1.2), on
293 * this state the SDRAM is in self-refresh mode.
294 *
295 * t0 = 0
296 * S5L8702 BOOTROM loads an IM3 image located at NOR:
297 * - IM3 header (first 0x800 bytes) is loaded at IRAM1_ORIG
298 * - IM3 body (decrypted RB bootloader) is loaded at IRAM0_ORIG
299 * The time needed to load the RB bootloader (~90 Kb) is estimated
300 * on 200~250 ms. Once executed, RB booloader moves itself from
301 * IRAM0_ORIG to IRAM1_ORIG+0x800, preserving current IM3 header
302 * that contains the NOR offset where the ONB (original NOR boot),
303 * is located (see dualboot.c for details).
304 *
305 * t1 = ~250 ms.
306 * If the PMU is hibernated, decrypted ONB (size 128Kb) is loaded
307 * and executed, it takes ~120 ms. Then the ONB restores the
308 * iPod to the state prior to hibernation.
309 * If not, initialize system and RB kernel, wait for t2.
310 *
311 * t2 = ~650 ms.
312 * Check user button selection.
313 * If OF, diagmode, or diskmode is selected then launch ONB.
314 * If not, wait for LCD initialization.
315 *
316 * t3 = ~700,~900 ms. (lcd_type_01,lcd_type_23)
317 * LCD is initialized, baclight ON.
318 * Wait for HDD spin-up.
319 *
320 * t4 = ~2600,~2800 ms.
321 * HDD is ready.
322 * If hold switch is locked, then load and launch ONB.
323 * If not, load rockbox.ipod file from HDD.
324 *
325 * t5 = ~2800,~3000 ms.
326 * rockbox.ipod is executed.
327 */
328void main(void)
329{
330 int fw = FW_ROCKBOX;
331 int rc = 0;
332 unsigned char *loadbuffer;
333 int (*kernel_entry)(void);
334
335 usec_timer_init();
336
337 /* Configure I2C0 */
338 i2c_preinit(0);
339
340 if (pmu_is_hibernated()) {
341 fw = FW_APPLE;
342 rc = launch_onb(1); /* 27/2 = 13.5 MHz. */
343 }
344
345 system_preinit();
346 memory_init();
347 /*
348 * XXX: BSS is initialized here, do not use .bss before this line
349 */
350 bss_init();
351
352 system_init();
353 kernel_init();
354 i2c_init();
355 power_init();
356
357 enable_irq();
358
359#ifdef HAVE_SERIAL
360 serial_setup();
361#endif
362
363 button_init();
364 if (rc == 0) {
365 /* User button selection timeout */
366 while (USEC_TIMER < 400000);
367 int btn = button_read_device();
368 /* This prevents HDD spin-up when the user enters DFU */
369 if (btn == (BUTTON_SELECT|BUTTON_MENU)) {
370 while (button_read_device() == (BUTTON_SELECT|BUTTON_MENU))
371 sleep(HZ/10);
372 sleep(HZ);
373 btn = button_read_device();
374 }
375 /* Enter OF, diagmode and diskmode using ONB */
376 if ((btn == BUTTON_MENU)
377 || (btn == (BUTTON_SELECT|BUTTON_LEFT))
378 || (btn == (BUTTON_SELECT|BUTTON_PLAY))) {
379 fw = FW_APPLE;
380 rc = kernel_launch_onb();
381 }
382 }
383
384 lcd_init();
385 lcd_set_foreground(LCD_WHITE);
386 lcd_set_background(LCD_BLACK);
387 lcd_clear_display();
388 font_init();
389 lcd_setfont(FONT_SYSFIXED);
390 lcd_update();
391 sleep(HZ/40);
392
393 verbose = true;
394
395 printf("Rockbox boot loader");
396 printf("Version: %s", rbversion);
397
398 backlight_init(); /* Turns on the backlight */
399
400 if (rc == 0) {
401 /* Wait until there is enought power to spin-up HDD */
402 battery_trap();
403
404 rc = storage_init();
405 if (rc != 0) {
406 printf("ATA error: %d", rc);
407 fatal_error(ERR_HDD);
408 }
409
410 filesystem_init();
411
412 /* We wait until HDD spins up to check for hold button */
413 if (button_hold()) {
414 fw = FW_APPLE;
415 printf("Executing OF...");
416 ata_sleepnow();
417 rc = kernel_launch_onb();
418 }
419 }
420
421 if (rc != 0) {
422 printf("Load OF error: %d", rc);
423 fatal_error(ERR_OF);
424 }
425
426#ifdef HAVE_BOOTLOADER_USB_MODE
427 /* Enter USB mode if SELECT+RIGHT are pressed */
428 if (button_read_device() == (BUTTON_SELECT|BUTTON_RIGHT))
429 usb_mode();
430#endif
431
432 rc = disk_mount_all();
433 if (rc <= 0) {
434 printf("No partition found");
435 fatal_error(ERR_RB);
436 }
437
438 printf("Loading Rockbox...");
439 loadbuffer = (unsigned char *)DRAM_ORIG;
440 rc = load_firmware(loadbuffer, BOOTFILE, MAX_LOADSIZE);
441
442 if (rc <= EFILE_EMPTY) {
443 printf("Error!");
444 printf("Can't load " BOOTFILE ": ");
445 printf(loader_strerror(rc));
446 fatal_error(ERR_RB);
447 }
448
449 printf("Rockbox loaded.");
450
451 /* If we get here, we have a new firmware image at 0x08000000, run it */
452 disable_irq();
453
454 kernel_entry = (void*) loadbuffer;
455 commit_discard_idcache();
456 rc = kernel_entry();
457
458 /* End stop - should not get here */
459 enable_irq();
460 printf("ERR: Failed to boot");
461 while(1);
462}