diff options
Diffstat (limited to 'bootloader/iriver_h300.c')
-rw-r--r-- | bootloader/iriver_h300.c | 393 |
1 files changed, 393 insertions, 0 deletions
diff --git a/bootloader/iriver_h300.c b/bootloader/iriver_h300.c new file mode 100644 index 0000000000..b2a667c8bd --- /dev/null +++ b/bootloader/iriver_h300.c | |||
@@ -0,0 +1,393 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2007 by Linus Nielsen Feltzing | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | #include "config.h" | ||
20 | |||
21 | #include <stdlib.h> | ||
22 | #include <stdio.h> | ||
23 | #include "inttypes.h" | ||
24 | #include "string.h" | ||
25 | #include "cpu.h" | ||
26 | #include "system.h" | ||
27 | #include "lcd.h" | ||
28 | #include "lcd-remote.h" | ||
29 | #include "kernel.h" | ||
30 | #include "thread.h" | ||
31 | #include "ata.h" | ||
32 | #include "usb.h" | ||
33 | #include "disk.h" | ||
34 | #include "font.h" | ||
35 | #include "adc.h" | ||
36 | #include "backlight.h" | ||
37 | #include "backlight-target.h" | ||
38 | #include "button.h" | ||
39 | #include "panic.h" | ||
40 | #include "power.h" | ||
41 | #include "file.h" | ||
42 | #include "uda1380.h" | ||
43 | #include "pcf50606.h" | ||
44 | #include "common.h" | ||
45 | #include "rbunicode.h" | ||
46 | |||
47 | #include <stdarg.h> | ||
48 | |||
49 | /* Maximum allowed firmware image size. 10MB is more than enough */ | ||
50 | #define MAX_LOADSIZE (10*1024*1024) | ||
51 | |||
52 | #define DRAM_START 0x31000000 | ||
53 | |||
54 | char version[] = APPSVERSION; | ||
55 | |||
56 | /* Reset the cookie for the crt0 crash check */ | ||
57 | inline void __reset_cookie(void) | ||
58 | { | ||
59 | asm(" move.l #0,%d0"); | ||
60 | asm(" move.l %d0,0x10017ffc"); | ||
61 | } | ||
62 | |||
63 | void start_iriver_fw(void) | ||
64 | { | ||
65 | asm(" move.w #0x2700,%sr"); | ||
66 | __reset_cookie(); | ||
67 | asm(" movec.l %d0,%vbr"); | ||
68 | asm(" move.l 0,%sp"); | ||
69 | asm(" lea.l 8,%a0"); | ||
70 | asm(" jmp (%a0)"); | ||
71 | } | ||
72 | |||
73 | void start_firmware(void) | ||
74 | { | ||
75 | asm(" move.w #0x2700,%sr"); | ||
76 | __reset_cookie(); | ||
77 | asm(" move.l %0,%%d0" :: "i"(DRAM_START)); | ||
78 | asm(" movec.l %d0,%vbr"); | ||
79 | asm(" move.l %0,%%sp" :: "m"(*(int *)DRAM_START)); | ||
80 | asm(" move.l %0,%%a0" :: "m"(*(int *)(DRAM_START+4))); | ||
81 | asm(" jmp (%a0)"); | ||
82 | } | ||
83 | |||
84 | void shutdown(void) | ||
85 | { | ||
86 | printf("Shutting down..."); | ||
87 | |||
88 | /* We need to gracefully spin down the disk to prevent clicks. */ | ||
89 | if (ide_powered()) | ||
90 | { | ||
91 | /* Make sure ATA has been initialized. */ | ||
92 | ata_init(); | ||
93 | |||
94 | /* And put the disk into sleep immediately. */ | ||
95 | ata_sleepnow(); | ||
96 | } | ||
97 | |||
98 | sleep(HZ*2); | ||
99 | |||
100 | __backlight_off(); | ||
101 | __remote_backlight_off(); | ||
102 | |||
103 | __reset_cookie(); | ||
104 | power_off(); | ||
105 | } | ||
106 | |||
107 | /* Print the battery voltage (and a warning message). */ | ||
108 | void check_battery(void) | ||
109 | { | ||
110 | int adc_battery, battery_voltage, batt_int, batt_frac; | ||
111 | |||
112 | adc_battery = adc_read(ADC_BATTERY); | ||
113 | |||
114 | battery_voltage = (adc_battery * BATTERY_SCALE_FACTOR) / 10000; | ||
115 | batt_int = battery_voltage / 100; | ||
116 | batt_frac = battery_voltage % 100; | ||
117 | |||
118 | printf("Batt: %d.%02dV", batt_int, batt_frac); | ||
119 | |||
120 | if (battery_voltage <= 310) | ||
121 | { | ||
122 | printf("WARNING! BATTERY LOW!!"); | ||
123 | sleep(HZ*2); | ||
124 | } | ||
125 | } | ||
126 | |||
127 | /* From the pcf50606 driver */ | ||
128 | extern unsigned char pcf50606_intregs[3]; | ||
129 | |||
130 | /* From common.c */ | ||
131 | extern int line; | ||
132 | extern int remote_line; | ||
133 | |||
134 | void main(void) | ||
135 | { | ||
136 | int i; | ||
137 | int rc; | ||
138 | bool rc_on_button = false; | ||
139 | bool on_button = false; | ||
140 | bool rec_button = false; | ||
141 | bool hold_status = false; | ||
142 | int data; | ||
143 | bool rtc_alarm; | ||
144 | int button; | ||
145 | |||
146 | /* We want to read the buttons as early as possible, before the user | ||
147 | releases the ON button */ | ||
148 | |||
149 | /* Set GPIO33, GPIO37, GPIO38 and GPIO52 as general purpose inputs | ||
150 | (The ON and Hold buttons on the main unit and the remote) */ | ||
151 | or_l(0x00100062, &GPIO1_FUNCTION); | ||
152 | and_l(~0x00100062, &GPIO1_ENABLE); | ||
153 | |||
154 | data = GPIO1_READ; | ||
155 | if ((data & 0x20) == 0) | ||
156 | on_button = true; | ||
157 | |||
158 | if ((data & 0x40) == 0) | ||
159 | rc_on_button = true; | ||
160 | |||
161 | /* Set the default state of the hard drive power to OFF */ | ||
162 | ide_power_enable(false); | ||
163 | |||
164 | power_init(); | ||
165 | |||
166 | /* Check the interrupt registers if it was an RTC alarm */ | ||
167 | rtc_alarm = (pcf50606_intregs[0] & 0x80)?true:false; | ||
168 | |||
169 | /* Turn off if we believe the start was accidental */ | ||
170 | if(!(rtc_alarm || on_button || rc_on_button || | ||
171 | usb_detect() || charger_inserted())) { | ||
172 | __reset_cookie(); | ||
173 | power_off(); | ||
174 | } | ||
175 | |||
176 | audiohw_reset(); | ||
177 | |||
178 | /* Start with the main backlight OFF. */ | ||
179 | __backlight_init(); | ||
180 | __backlight_off(); | ||
181 | |||
182 | __remote_backlight_on(); | ||
183 | |||
184 | system_init(); | ||
185 | kernel_init(); | ||
186 | |||
187 | /* Set up waitstates for the peripherals */ | ||
188 | set_cpu_frequency(0); /* PLL off */ | ||
189 | coldfire_set_pllcr_audio_bits(DEFAULT_PLLCR_AUDIO_BITS); | ||
190 | set_irq_level(0); | ||
191 | |||
192 | adc_init(); | ||
193 | button_init(); | ||
194 | |||
195 | if ((on_button && button_hold()) || | ||
196 | (rc_on_button && remote_button_hold())) | ||
197 | { | ||
198 | hold_status = true; | ||
199 | } | ||
200 | |||
201 | backlight_init(); | ||
202 | |||
203 | lcd_init(); | ||
204 | lcd_remote_init(); | ||
205 | font_init(); | ||
206 | |||
207 | lcd_setfont(FONT_SYSFIXED); | ||
208 | |||
209 | printf("Rockbox boot loader"); | ||
210 | printf("Version %s", version); | ||
211 | |||
212 | sleep(HZ/50); /* Allow the button driver to check the buttons */ | ||
213 | rec_button = ((button_status() & BUTTON_REC) == BUTTON_REC) | ||
214 | || ((button_status() & BUTTON_RC_REC) == BUTTON_RC_REC); | ||
215 | |||
216 | check_battery(); | ||
217 | |||
218 | /* Don't start if the Hold button is active on the device you | ||
219 | are starting with */ | ||
220 | if (!usb_detect() && !charger_inserted() && hold_status) | ||
221 | { | ||
222 | if (detect_original_firmware()) | ||
223 | { | ||
224 | printf("Hold switch on"); | ||
225 | shutdown(); | ||
226 | } | ||
227 | } | ||
228 | |||
229 | if(rtc_alarm) | ||
230 | printf("RTC alarm detected"); | ||
231 | |||
232 | /* Holding REC while starting runs the original firmware */ | ||
233 | if (detect_original_firmware() && rec_button) | ||
234 | { | ||
235 | printf("Starting original firmware..."); | ||
236 | start_iriver_fw(); | ||
237 | } | ||
238 | |||
239 | if(charger_inserted()) | ||
240 | { | ||
241 | const char charging_msg[] = "Charging..."; | ||
242 | const char complete_msg[] = "Charging complete"; | ||
243 | const char *msg; | ||
244 | int w, h; | ||
245 | bool blink_toggle = false; | ||
246 | bool request_start = false; | ||
247 | |||
248 | while(charger_inserted() && !request_start) | ||
249 | { | ||
250 | button = button_get_w_tmo(HZ); | ||
251 | |||
252 | switch(button) | ||
253 | { | ||
254 | case BUTTON_ON: | ||
255 | request_start = true; | ||
256 | break; | ||
257 | |||
258 | case BUTTON_NONE: /* Timeout */ | ||
259 | |||
260 | if(charging_state()) | ||
261 | { | ||
262 | /* To be replaced with a nice animation */ | ||
263 | blink_toggle = !blink_toggle; | ||
264 | msg = charging_msg; | ||
265 | } | ||
266 | else | ||
267 | { | ||
268 | blink_toggle = true; | ||
269 | msg = complete_msg; | ||
270 | } | ||
271 | |||
272 | font_getstringsize(msg, &w, &h, FONT_SYSFIXED); | ||
273 | reset_screen(); | ||
274 | if(blink_toggle) | ||
275 | lcd_putsxy((LCD_WIDTH-w)/2, (LCD_HEIGHT-h)/2, msg); | ||
276 | |||
277 | check_battery(); | ||
278 | break; | ||
279 | } | ||
280 | |||
281 | if(usb_detect()) | ||
282 | request_start = true; | ||
283 | } | ||
284 | if(!request_start) | ||
285 | { | ||
286 | __reset_cookie(); | ||
287 | power_off(); | ||
288 | } | ||
289 | } | ||
290 | |||
291 | usb_init(); | ||
292 | |||
293 | /* A hack to enter USB mode without using the USB thread */ | ||
294 | if(usb_detect()) | ||
295 | { | ||
296 | const char msg[] = "Bootloader USB mode"; | ||
297 | int w, h; | ||
298 | font_getstringsize(msg, &w, &h, FONT_SYSFIXED); | ||
299 | reset_screen(); | ||
300 | lcd_putsxy((LCD_WIDTH-w)/2, (LCD_HEIGHT-h)/2, msg); | ||
301 | lcd_update(); | ||
302 | |||
303 | lcd_remote_puts(0, 3, msg); | ||
304 | lcd_remote_update(); | ||
305 | |||
306 | ide_power_enable(true); | ||
307 | ata_enable(false); | ||
308 | sleep(HZ/20); | ||
309 | usb_enable(true); | ||
310 | cpu_idle_mode(true); | ||
311 | while (usb_detect()) | ||
312 | { | ||
313 | /* Print the battery status. */ | ||
314 | line = 0; | ||
315 | remote_line = 0; | ||
316 | check_battery(); | ||
317 | |||
318 | ata_spin(); /* Prevent the drive from spinning down */ | ||
319 | sleep(HZ); | ||
320 | } | ||
321 | |||
322 | cpu_idle_mode(false); | ||
323 | usb_enable(false); | ||
324 | |||
325 | reset_screen(); | ||
326 | lcd_update(); | ||
327 | } | ||
328 | |||
329 | rc = ata_init(); | ||
330 | if(rc) | ||
331 | { | ||
332 | reset_screen(); | ||
333 | printf("ATA error: %d", rc); | ||
334 | printf("Insert USB cable and press"); | ||
335 | printf("a button"); | ||
336 | while(!(button_get(true) & BUTTON_REL)); | ||
337 | } | ||
338 | |||
339 | |||
340 | disk_init(); | ||
341 | |||
342 | rc = disk_mount_all(); | ||
343 | if (rc<=0) | ||
344 | { | ||
345 | reset_screen(); | ||
346 | printf("No partition found"); | ||
347 | while(button_get(true) != SYS_USB_CONNECTED) {}; | ||
348 | } | ||
349 | |||
350 | printf("Loading firmware"); | ||
351 | i = load_firmware((unsigned char *)DRAM_START, BOOTFILE, MAX_LOADSIZE); | ||
352 | if(i < 0) | ||
353 | printf("Error: %d", strerror(i)); | ||
354 | |||
355 | if (i == EOK) | ||
356 | start_firmware(); | ||
357 | |||
358 | if (!detect_original_firmware()) | ||
359 | { | ||
360 | printf("No firmware found on disk"); | ||
361 | sleep(HZ*2); | ||
362 | shutdown(); | ||
363 | } | ||
364 | else { | ||
365 | sleep(HZ*2); | ||
366 | start_iriver_fw(); | ||
367 | } | ||
368 | } | ||
369 | |||
370 | /* These functions are present in the firmware library, but we reimplement | ||
371 | them here because the originals do a lot more than we want */ | ||
372 | void screen_dump(void) | ||
373 | { | ||
374 | } | ||
375 | |||
376 | int usb_screen(void) | ||
377 | { | ||
378 | return 0; | ||
379 | } | ||
380 | |||
381 | unsigned short *bidi_l2v(const unsigned char *str, int orientation) | ||
382 | { | ||
383 | static unsigned short utf16_buf[SCROLL_LINE_SIZE]; | ||
384 | unsigned short *target; | ||
385 | (void)orientation; | ||
386 | |||
387 | target = utf16_buf; | ||
388 | |||
389 | while (*str) | ||
390 | str = utf8decode(str, target++); | ||
391 | *target = 0; | ||
392 | return utf16_buf; | ||
393 | } | ||