diff options
Diffstat (limited to 'bootloader/ipod6g.c')
-rw-r--r-- | bootloader/ipod6g.c | 462 |
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 | |||
79 | extern void bss_init(void); | ||
80 | extern uint32_t _movestart; | ||
81 | extern uint32_t start_loc; | ||
82 | |||
83 | extern int line; | ||
84 | |||
85 | #ifdef HAVE_BOOTLOADER_USB_MODE | ||
86 | static 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 | |||
147 | void 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 | |||
183 | static 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 | |||
243 | static 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 */ | ||
275 | static 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 | |||
283 | static 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 | */ | ||
328 | void 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 | } | ||