blob: d09a227d17509aed8b97d6cecac4878a3c01da87 [file] [log] [blame]
Dave Chapman38e8fb62005-11-08 00:52:39 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 by Dave Chapman
11 *
12 * Based on Rockbox iriver bootloader by Linus Nielsen Feltzing
13 * and the ipodlinux bootloader by Daniel Palffy and Bernard Leach
14 *
15 * All files in this archive are subject to the GNU General Public License.
16 * See the file COPYING in the source tree root for full license agreement.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ****************************************************************************/
22#include "config.h"
23
24#include <stdlib.h>
25#include <stdio.h>
Dave Chapman061f3802005-11-13 20:59:30 +000026#include <string.h>
Dave Chapman38e8fb62005-11-08 00:52:39 +000027#include "cpu.h"
28#include "system.h"
29#include "lcd.h"
30#include "kernel.h"
31#include "thread.h"
32#include "ata.h"
33#include "fat.h"
34#include "disk.h"
35#include "font.h"
36#include "adc.h"
37#include "backlight.h"
Dave Chapman38e8fb62005-11-08 00:52:39 +000038#include "panic.h"
39#include "power.h"
40#include "file.h"
41
42#define DRAM_START 0x10000000
43#define IPOD_PP5020_RTC 0x60005010
44
45#define IPOD_HW_REVISION (*((volatile unsigned long*)(0x00002084)))
46
Dave Chapman061f3802005-11-13 20:59:30 +000047#define BUTTON_LEFT 1
48#define BUTTON_MENU 2
49#define BUTTON_RIGHT 3
50#define BUTTON_PLAY 4
51
52/* Size of the buffer to store the loaded Rockbox/Linux image */
53#define MAX_LOADSIZE (4*1024*1024)
54
Dave Chapman38e8fb62005-11-08 00:52:39 +000055char version[] = APPSVERSION;
56
Dave Chapman38e8fb62005-11-08 00:52:39 +000057typedef struct _image {
58 unsigned type; /* '' */
59 unsigned id; /* */
60 unsigned pad1; /* 0000 0000 */
61 unsigned devOffset; /* byte offset of start of image code */
62 unsigned len; /* length in bytes of image */
63 void *addr; /* load address */
64 unsigned entryOffset; /* execution start within image */
65 unsigned chksum; /* checksum for image */
66 unsigned vers; /* image version */
67 unsigned loadAddr; /* load address for image */
68} image_t;
69
70extern image_t boot_table[];
71
72int line=0;
73
74static void memmove16(void *dest, const void *src, unsigned count)
75{
76 struct bufstr {
77 unsigned _buf[4];
78 } *d, *s;
79
80 if (src >= dest) {
81 count = (count + 15) >> 4;
82 d = (struct bufstr *) dest;
83 s = (struct bufstr *) src;
84 while (count--)
85 *d++ = *s++;
86 } else {
87 count = (count + 15) >> 4;
88 d = (struct bufstr *)(dest + (count <<4));
89 s = (struct bufstr *)(src + (count <<4));
90 while (count--)
91 *--d = *--s;
92 }
93}
94
95/* get current usec counter */
96int timer_get_current(void)
97{
98 return inl(IPOD_PP5020_RTC);
99}
100
101/* check if number of seconds has past */
102int timer_check(int clock_start, unsigned int usecs)
103{
104 if ((inl(IPOD_PP5020_RTC) - clock_start) >= usecs) {
105 return 1;
106 } else {
107 return 0;
108 }
109}
110
111/* This isn't a sleep, but let's call it that. */
112int usleep(unsigned int usecs)
113{
114 unsigned int start = inl(IPOD_PP5020_RTC);
115
116 while ((inl(IPOD_PP5020_RTC) - start) < usecs) {
117 // empty
118 }
119
120 return 0;
121}
122
Dave Chapman061f3802005-11-13 20:59:30 +0000123
124static void ser_opto_keypad_cfg(int val)
125{
126 int start_time;
127
128 outl(inl(0x6000d004) & ~0x80, 0x6000d004);
129
130 outl(inl(0x7000c104) | 0xc000000, 0x7000c104);
131 outl(val, 0x7000c120);
132 outl(inl(0x7000c100) | 0x80000000, 0x7000c100);
133
134 outl(inl(0x6000d024) & ~0x10, 0x6000d024);
135 outl(inl(0x6000d014) | 0x10, 0x6000d014);
136
137 start_time = timer_get_current();
138 do {
139 if ((inl(0x7000c104) & 0x80000000) == 0) {
140 break;
141 }
142 } while (timer_check(start_time, 1500) != 0);
143
144 outl(inl(0x7000c100) & ~0x80000000, 0x7000c100);
145
146 outl(inl(0x6000d004) | 0x80, 0x6000d004);
147 outl(inl(0x6000d024) | 0x10, 0x6000d024);
148 outl(inl(0x6000d014) & ~0x10, 0x6000d014);
149
150 outl(inl(0x7000c104) | 0xc000000, 0x7000c104);
151 outl(inl(0x7000c100) | 0x60000000, 0x7000c100);
152}
153
154int opto_keypad_read(void)
155{
156 int loop_cnt, had_io = 0;
157
158 for (loop_cnt = 5; loop_cnt != 0;)
159 {
160 int key_pressed = 0;
161 int start_time;
162 unsigned int key_pad_val;
163
164 ser_opto_keypad_cfg(0x8000023a);
165
166 start_time = timer_get_current();
167 do {
168 if (inl(0x7000c104) & 0x4000000) {
169 had_io = 1;
170 break;
171 }
172
173 if (had_io != 0) {
174 break;
175 }
176 } while (timer_check(start_time, 1500) != 0);
177
178 key_pad_val = inl(0x7000c140);
179 if ((key_pad_val & ~0x7fff0000) != 0x8000023a) {
180 loop_cnt--;
181 } else {
182 key_pad_val = (key_pad_val << 11) >> 27;
183 key_pressed = 1;
184 }
185
186 outl(inl(0x7000c100) | 0x60000000, 0x7000c100);
187 outl(inl(0x7000c104) | 0xc000000, 0x7000c104);
188
189 if (key_pressed != 0) {
190 return key_pad_val ^ 0x1f;
191 }
192 }
193
194 return 0;
195}
196
197static int key_pressed(void)
198{
199 unsigned char state;
200
201 state = opto_keypad_read();
202 if ((state & 0x4) == 0) return BUTTON_LEFT;
203 if ((state & 0x10) == 0) return BUTTON_MENU;
204 if ((state & 0x8) == 0) return BUTTON_PLAY;
205 if ((state & 0x2) == 0) return BUTTON_RIGHT;
206 return 0;
207}
208
209int load_rockbox(unsigned char* buf)
Dave Chapman38e8fb62005-11-08 00:52:39 +0000210{
211 int fd;
212 int rc;
213 int len;
214 unsigned long chksum;
215 char model[5];
216 unsigned long sum;
217 int i;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000218 char str[80];
219
Dave Chapman20eed882005-11-20 10:57:53 +0000220 fd = open("/.rockbox/" BOOTFILE, O_RDONLY);
Dave Chapman38e8fb62005-11-08 00:52:39 +0000221 if(fd < 0)
Dave Chapman20eed882005-11-20 10:57:53 +0000222 {
223 fd = open("/" BOOTFILE, O_RDONLY);
224 if(fd < 0)
225 return -1;
226 }
Dave Chapman38e8fb62005-11-08 00:52:39 +0000227
228 len = filesize(fd) - 8;
229
Dave Chapman061f3802005-11-13 20:59:30 +0000230 if (len > MAX_LOADSIZE)
231 return -6;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000232
233 lseek(fd, FIRMWARE_OFFSET_FILE_CRC, SEEK_SET);
234
235 rc = read(fd, &chksum, 4);
Dave Chapman061f3802005-11-13 20:59:30 +0000236 chksum=betoh32(chksum); /* Rockbox checksums are big-endian */
Dave Chapman38e8fb62005-11-08 00:52:39 +0000237 if(rc < 4)
Dave Chapman061f3802005-11-13 20:59:30 +0000238 return -2;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000239
240 rc = read(fd, model, 4);
241 if(rc < 4)
Dave Chapman061f3802005-11-13 20:59:30 +0000242 return -3;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000243
244 model[4] = 0;
245
Thom Johansen1c69caf2006-01-18 23:02:00 +0000246 snprintf(str, 80, "Model: %s", model);
247 lcd_puts(0, line++, str);
248 snprintf(str, 80, "Checksum: %x", chksum);
Dave Chapman38e8fb62005-11-08 00:52:39 +0000249 lcd_puts(0, line++, str);
250 lcd_update();
251
252 lseek(fd, FIRMWARE_OFFSET_FILE_DATA, SEEK_SET);
253
254 rc = read(fd, buf, len);
255 if(rc < len)
Dave Chapman061f3802005-11-13 20:59:30 +0000256 return -4;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000257
258 close(fd);
259
260 sum = MODEL_NUMBER;
261
262 for(i = 0;i < len;i++) {
Dave Chapman061f3802005-11-13 20:59:30 +0000263 sum += buf[i];
Dave Chapman38e8fb62005-11-08 00:52:39 +0000264 }
265
266 snprintf(str, 80, "Sum: %x", sum);
267 lcd_puts(0, line++, str);
268 lcd_update();
269
270 if(sum != chksum)
271 return -5;
272
Dave Chapman061f3802005-11-13 20:59:30 +0000273 return len;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000274}
275
Dave Chapman061f3802005-11-13 20:59:30 +0000276
277int load_linux(unsigned char* buf) {
278 int fd;
279 int rc;
280 int len;
281 char str[80];
282
283 fd=open("/linux.bin",O_RDONLY);
284 if (fd < 0)
285 return -1;
286
287 len=filesize(fd);
288 if (len > MAX_LOADSIZE)
289 return -6;
290
291 rc=read(fd,buf,len);
292
293 if (rc < len)
294 return -4;
295
296 snprintf(str, 80, "Loaded Linux: %d bytes", len);
297 lcd_puts(0, line++, str);
298 lcd_update();
299
300 return len;
301}
302
303
304/* A buffer to load the Linux kernel or Rockbox into */
305unsigned char loadbuffer[MAX_LOADSIZE];
306
Dave Chapman38e8fb62005-11-08 00:52:39 +0000307void* main(void)
308{
309 char buf[256];
310 int imageno=0;
311 int i;
312 int rc;
313 int padding = 0x4400;
314 image_t *tblp = boot_table;
315 void* entry;
316 struct partinfo* pinfo;
317 unsigned short* identify_info;
318
319 /* Turn on the backlight */
320
321#if CONFIG_BACKLIGHT==BL_IPOD4G
Dave Chapman38e8fb62005-11-08 00:52:39 +0000322 /* brightness full */
323 outl(0x80000000 | (0xff << 16), 0x7000a010);
324
Dave Chapmand8c5d1b2005-11-08 09:07:13 +0000325 /* set port B03 on */
Dave Chapman38e8fb62005-11-08 00:52:39 +0000326 outl(((0x100 | 1) << 3), 0x6000d824);
327
328#elif CONFIG_BACKLIGHT==BL_IPODNANO
329
330 /* set port B03 on */
331 outl(((0x100 | 1) << 3), 0x6000d824);
332
333 /* set port L07 on */
334 outl(((0x100 | 1) << 7), 0x6000d12c);
335
336#endif
337
338 system_init();
339 kernel_init();
340 lcd_init();
341 font_init();
342
343#if 0
344 /* ADC and button drivers are not yet implemented */
345 adc_init();
346 button_init();
347#endif
348
Dave Chapman3ded3ce2006-01-18 19:52:02 +0000349 line=0;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000350
351 lcd_setfont(FONT_SYSFIXED);
352
Dave Chapman3ded3ce2006-01-18 19:52:02 +0000353 lcd_puts(0, line++, "Rockbox boot loader");
Dave Chapman38e8fb62005-11-08 00:52:39 +0000354 snprintf(buf, sizeof(buf), "Version: 20%s", version);
355 lcd_puts(0, line++, buf);
356 snprintf(buf, sizeof(buf), "IPOD version: 0x%08x", IPOD_HW_REVISION);
357 lcd_puts(0, line++, buf);
358 lcd_update();
359
360 i=ata_init();
361 if (i==0) {
362 identify_info=ata_get_identify();
363 /* Show model */
364 for (i=0; i < 20; i++) {
365 ((unsigned short*)buf)[i]=htobe16(identify_info[i+27]);
366 }
367 buf[40]=0;
368 for (i=39; i && buf[i]==' '; i--) {
369 buf[i]=0;
370 }
371 lcd_puts(0, line++, buf);
372 lcd_update();
373 } else {
374 snprintf(buf, sizeof(buf), "ATA: %d", i);
375 lcd_puts(0, line++, buf);
376 lcd_update();
377 }
378
379 disk_init();
380 rc = disk_mount_all();
381 if (rc<=0)
382 {
383 lcd_puts(0, line++, "No partition found");
384 lcd_update();
385// while(button_get(true) != SYS_USB_CONNECTED) {};
386 }
387
388 pinfo = disk_partinfo(1);
389 snprintf(buf, sizeof(buf), "Partition 1: 0x%02x %ld MB",
390 pinfo->type, pinfo->size / 2048);
391 lcd_puts(0, line++, buf);
392 lcd_update();
393
Dave Chapman061f3802005-11-13 20:59:30 +0000394 /* Check for a keypress */
395 i=key_pressed();
Dave Chapman38e8fb62005-11-08 00:52:39 +0000396
Dave Chapman20eed882005-11-20 10:57:53 +0000397 if ((i!=BUTTON_MENU) && (i!=BUTTON_PLAY)) {
Dave Chapman061f3802005-11-13 20:59:30 +0000398 lcd_puts(0, line, "Loading Rockbox...");
399 lcd_update();
400 rc=load_rockbox(loadbuffer);
401 if (rc < 0) {
402 snprintf(buf, sizeof(buf), "Rockbox error: %d",rc);
403 lcd_puts(0, line++, buf);
404 lcd_update();
405 } else {
406 lcd_puts(0, line++, "Rockbox loaded.");
407 lcd_update();
Dave Chapman061f3802005-11-13 20:59:30 +0000408 memcpy((void*)DRAM_START,loadbuffer,rc);
Dave Chapmancb7e6952006-01-05 17:02:48 +0000409
410 /* Transfer execution directly to Rockbox - we don't want
411 to run the rest of the bootloader startup code. */
412 asm volatile(
413 "mov r0, #0x10000000 \n"
414 "mov pc, r0 \n"
415 );
416
417 /* We don't get here, but keep the compiler happy. */
418 return (void*)0;
Dave Chapman061f3802005-11-13 20:59:30 +0000419 }
420 }
421
422 if (i==BUTTON_PLAY) {
423 lcd_puts(0, line, "Loading Linux...");
424 lcd_update();
425 rc=load_linux(loadbuffer);
426 if (rc < 0) {
427 snprintf(buf, sizeof(buf), "Linux error: %d",rc);
428 lcd_puts(0, line++, buf);
429 lcd_update();
430 } else {
431 memcpy((void*)DRAM_START,loadbuffer,rc);
432 return (void*)DRAM_START;
433 }
434 }
Dave Chapman38e8fb62005-11-08 00:52:39 +0000435
436 /* If everything else failed, try the original firmware */
Dave Chapman061f3802005-11-13 20:59:30 +0000437
Dave Chapman38e8fb62005-11-08 00:52:39 +0000438 lcd_puts(0, line, "Loading original firmware...");
439 lcd_update();
440
Dave Chapman061f3802005-11-13 20:59:30 +0000441 /* Pause for 5 seconds so we can see what's happened */
Dave Chapman20eed882005-11-20 10:57:53 +0000442// usleep(5000000);
Dave Chapman061f3802005-11-13 20:59:30 +0000443
Dave Chapman38e8fb62005-11-08 00:52:39 +0000444 entry = tblp->addr + tblp->entryOffset;
445 if (imageno || ((int)tblp->addr & 0xffffff) != 0) {
446 memmove16(tblp->addr, tblp->addr + tblp->devOffset - padding,
447 tblp->len);
448 }
449
450 /* Return the start address in loaded image */
451 return entry;
452}
453
454/* These functions are present in the firmware library, but we reimplement
455 them here because the originals do a lot more than we want */
456
457void reset_poweroff_timer(void)
458{
459}
460
Dave Chapman38e8fb62005-11-08 00:52:39 +0000461int dbg_ports(void)
462{
463 return 0;
464}
465
466void mpeg_stop(void)
467{
468}
469
470void usb_acknowledge(void)
471{
472}
473
474void usb_wait_for_disconnect(void)
475{
476}
477
478void sys_poweroff(void)
479{
480}