blob: 793253a0ca51885cf6ac0eaaf6294e8b6f7cc0d6 [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
Dave Chapman8adc81d2006-02-21 21:13:03 +000042#define XSC(X) #X
43#define SC(X) XSC(X)
44
Dave Chapmanfa743562006-02-05 19:05:55 +000045#if (CONFIG_CPU == PP5020)
46#define DRAM_START 0x10000000
47#else
48#define IPOD_LCD_BASE 0xc0001000
49#define DRAM_START 0x28000000
50#endif
Dave Chapman38e8fb62005-11-08 00:52:39 +000051#define IPOD_HW_REVISION (*((volatile unsigned long*)(0x00002084)))
52
Dave Chapmanf9dac402006-01-31 01:50:07 +000053/* We copy the hardware revision to the last four bytes of SDRAM and then
54 re-read it after we have re-mapped SDRAM to 0x0 in Rockbox */
55#define TMP_IPOD_HW_REVISION (*((volatile unsigned long*)(0x11fffffc)))
56
Dave Chapman061f3802005-11-13 20:59:30 +000057#define BUTTON_LEFT 1
58#define BUTTON_MENU 2
59#define BUTTON_RIGHT 3
60#define BUTTON_PLAY 4
Dave Chapmanfa743562006-02-05 19:05:55 +000061#define BUTTON_HOLD 5
Dave Chapman061f3802005-11-13 20:59:30 +000062
63/* Size of the buffer to store the loaded Rockbox/Linux image */
64#define MAX_LOADSIZE (4*1024*1024)
65
Dave Chapman38e8fb62005-11-08 00:52:39 +000066char version[] = APPSVERSION;
67
Dave Chapman38e8fb62005-11-08 00:52:39 +000068typedef struct _image {
69 unsigned type; /* '' */
70 unsigned id; /* */
71 unsigned pad1; /* 0000 0000 */
72 unsigned devOffset; /* byte offset of start of image code */
73 unsigned len; /* length in bytes of image */
74 void *addr; /* load address */
75 unsigned entryOffset; /* execution start within image */
76 unsigned chksum; /* checksum for image */
77 unsigned vers; /* image version */
78 unsigned loadAddr; /* load address for image */
79} image_t;
80
81extern image_t boot_table[];
82
83int line=0;
84
85static void memmove16(void *dest, const void *src, unsigned count)
86{
87 struct bufstr {
88 unsigned _buf[4];
89 } *d, *s;
90
91 if (src >= dest) {
92 count = (count + 15) >> 4;
93 d = (struct bufstr *) dest;
94 s = (struct bufstr *) src;
95 while (count--)
96 *d++ = *s++;
97 } else {
98 count = (count + 15) >> 4;
99 d = (struct bufstr *)(dest + (count <<4));
100 s = (struct bufstr *)(src + (count <<4));
101 while (count--)
102 *--d = *--s;
103 }
104}
105
Dave Chapmanfa743562006-02-05 19:05:55 +0000106#if CONFIG_KEYPAD == IPOD_4G_PAD
Dave Chapman38e8fb62005-11-08 00:52:39 +0000107/* check if number of seconds has past */
108int timer_check(int clock_start, unsigned int usecs)
109{
Dave Chapmanfa743562006-02-05 19:05:55 +0000110 if ((USEC_TIMER - clock_start) >= usecs) {
Dave Chapman38e8fb62005-11-08 00:52:39 +0000111 return 1;
112 } else {
113 return 0;
114 }
115}
116
Dave Chapman061f3802005-11-13 20:59:30 +0000117static void ser_opto_keypad_cfg(int val)
118{
119 int start_time;
120
121 outl(inl(0x6000d004) & ~0x80, 0x6000d004);
122
123 outl(inl(0x7000c104) | 0xc000000, 0x7000c104);
124 outl(val, 0x7000c120);
125 outl(inl(0x7000c100) | 0x80000000, 0x7000c100);
126
127 outl(inl(0x6000d024) & ~0x10, 0x6000d024);
128 outl(inl(0x6000d014) | 0x10, 0x6000d014);
129
Dave Chapmanfa743562006-02-05 19:05:55 +0000130 start_time = USEC_TIMER;
Dave Chapman061f3802005-11-13 20:59:30 +0000131 do {
132 if ((inl(0x7000c104) & 0x80000000) == 0) {
133 break;
134 }
135 } while (timer_check(start_time, 1500) != 0);
136
137 outl(inl(0x7000c100) & ~0x80000000, 0x7000c100);
138
139 outl(inl(0x6000d004) | 0x80, 0x6000d004);
140 outl(inl(0x6000d024) | 0x10, 0x6000d024);
141 outl(inl(0x6000d014) & ~0x10, 0x6000d014);
142
143 outl(inl(0x7000c104) | 0xc000000, 0x7000c104);
144 outl(inl(0x7000c100) | 0x60000000, 0x7000c100);
145}
146
147int opto_keypad_read(void)
148{
149 int loop_cnt, had_io = 0;
150
151 for (loop_cnt = 5; loop_cnt != 0;)
152 {
153 int key_pressed = 0;
154 int start_time;
155 unsigned int key_pad_val;
156
157 ser_opto_keypad_cfg(0x8000023a);
158
Dave Chapmanfa743562006-02-05 19:05:55 +0000159 start_time = USEC_TIMER;
Dave Chapman061f3802005-11-13 20:59:30 +0000160 do {
161 if (inl(0x7000c104) & 0x4000000) {
162 had_io = 1;
163 break;
164 }
165
166 if (had_io != 0) {
167 break;
168 }
169 } while (timer_check(start_time, 1500) != 0);
170
171 key_pad_val = inl(0x7000c140);
172 if ((key_pad_val & ~0x7fff0000) != 0x8000023a) {
173 loop_cnt--;
174 } else {
175 key_pad_val = (key_pad_val << 11) >> 27;
176 key_pressed = 1;
177 }
178
179 outl(inl(0x7000c100) | 0x60000000, 0x7000c100);
180 outl(inl(0x7000c104) | 0xc000000, 0x7000c104);
181
182 if (key_pressed != 0) {
183 return key_pad_val ^ 0x1f;
184 }
185 }
186
187 return 0;
188}
Dave Chapmanfa743562006-02-05 19:05:55 +0000189#endif
Dave Chapman061f3802005-11-13 20:59:30 +0000190
191static int key_pressed(void)
192{
193 unsigned char state;
194
Dave Chapmanfa743562006-02-05 19:05:55 +0000195#if CONFIG_KEYPAD == IPOD_4G_PAD
Dave Chapman28507912006-02-27 12:35:05 +0000196#if defined(APPLE_IPODMINI)
197 state = GPIOA_INPUT_VAL & 0x3f;
198#else
Dave Chapman061f3802005-11-13 20:59:30 +0000199 state = opto_keypad_read();
Dave Chapman28507912006-02-27 12:35:05 +0000200#endif
Dave Chapman061f3802005-11-13 20:59:30 +0000201 if ((state & 0x4) == 0) return BUTTON_LEFT;
202 if ((state & 0x10) == 0) return BUTTON_MENU;
203 if ((state & 0x8) == 0) return BUTTON_PLAY;
204 if ((state & 0x2) == 0) return BUTTON_RIGHT;
Dave Chapmanfa743562006-02-05 19:05:55 +0000205#elif CONFIG_KEYPAD == IPOD_3G_PAD
206 state = inb(0xcf000030);
207 if (((state & 0x20) == 0)) return BUTTON_HOLD; /* hold on */
208 if ((state & 0x08) == 0) return BUTTON_LEFT;
209 if ((state & 0x10) == 0) return BUTTON_MENU;
210 if ((state & 0x04) == 0) return BUTTON_PLAY;
211 if ((state & 0x01) == 0) return BUTTON_RIGHT;
212#endif
Dave Chapman061f3802005-11-13 20:59:30 +0000213 return 0;
214}
215
216int load_rockbox(unsigned char* buf)
Dave Chapman38e8fb62005-11-08 00:52:39 +0000217{
218 int fd;
219 int rc;
220 int len;
221 unsigned long chksum;
222 char model[5];
223 unsigned long sum;
224 int i;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000225 char str[80];
226
Dave Chapman20eed882005-11-20 10:57:53 +0000227 fd = open("/.rockbox/" BOOTFILE, O_RDONLY);
Dave Chapman38e8fb62005-11-08 00:52:39 +0000228 if(fd < 0)
Dave Chapman20eed882005-11-20 10:57:53 +0000229 {
230 fd = open("/" BOOTFILE, O_RDONLY);
231 if(fd < 0)
232 return -1;
233 }
Dave Chapman38e8fb62005-11-08 00:52:39 +0000234
235 len = filesize(fd) - 8;
236
Dave Chapman061f3802005-11-13 20:59:30 +0000237 if (len > MAX_LOADSIZE)
238 return -6;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000239
240 lseek(fd, FIRMWARE_OFFSET_FILE_CRC, SEEK_SET);
241
242 rc = read(fd, &chksum, 4);
Dave Chapman061f3802005-11-13 20:59:30 +0000243 chksum=betoh32(chksum); /* Rockbox checksums are big-endian */
Dave Chapman38e8fb62005-11-08 00:52:39 +0000244 if(rc < 4)
Dave Chapman061f3802005-11-13 20:59:30 +0000245 return -2;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000246
247 rc = read(fd, model, 4);
248 if(rc < 4)
Dave Chapman061f3802005-11-13 20:59:30 +0000249 return -3;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000250
251 model[4] = 0;
252
Thom Johansen1c69caf2006-01-18 23:02:00 +0000253 snprintf(str, 80, "Model: %s", model);
254 lcd_puts(0, line++, str);
255 snprintf(str, 80, "Checksum: %x", chksum);
Dave Chapman38e8fb62005-11-08 00:52:39 +0000256 lcd_puts(0, line++, str);
257 lcd_update();
258
259 lseek(fd, FIRMWARE_OFFSET_FILE_DATA, SEEK_SET);
260
261 rc = read(fd, buf, len);
262 if(rc < len)
Dave Chapman061f3802005-11-13 20:59:30 +0000263 return -4;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000264
265 close(fd);
266
267 sum = MODEL_NUMBER;
268
269 for(i = 0;i < len;i++) {
Dave Chapman061f3802005-11-13 20:59:30 +0000270 sum += buf[i];
Dave Chapman38e8fb62005-11-08 00:52:39 +0000271 }
272
273 snprintf(str, 80, "Sum: %x", sum);
274 lcd_puts(0, line++, str);
275 lcd_update();
276
277 if(sum != chksum)
278 return -5;
279
Dave Chapman061f3802005-11-13 20:59:30 +0000280 return len;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000281}
282
Dave Chapman061f3802005-11-13 20:59:30 +0000283
284int load_linux(unsigned char* buf) {
285 int fd;
286 int rc;
287 int len;
288 char str[80];
289
290 fd=open("/linux.bin",O_RDONLY);
291 if (fd < 0)
292 return -1;
293
294 len=filesize(fd);
295 if (len > MAX_LOADSIZE)
296 return -6;
297
298 rc=read(fd,buf,len);
299
300 if (rc < len)
301 return -4;
302
303 snprintf(str, 80, "Loaded Linux: %d bytes", len);
304 lcd_puts(0, line++, str);
305 lcd_update();
306
307 return len;
308}
309
310
311/* A buffer to load the Linux kernel or Rockbox into */
312unsigned char loadbuffer[MAX_LOADSIZE];
313
Dave Chapman38e8fb62005-11-08 00:52:39 +0000314void* main(void)
315{
316 char buf[256];
317 int imageno=0;
318 int i;
319 int rc;
320 int padding = 0x4400;
321 image_t *tblp = boot_table;
322 void* entry;
323 struct partinfo* pinfo;
324 unsigned short* identify_info;
325
326 /* Turn on the backlight */
327
328#if CONFIG_BACKLIGHT==BL_IPOD4G
Dave Chapman38e8fb62005-11-08 00:52:39 +0000329 /* brightness full */
330 outl(0x80000000 | (0xff << 16), 0x7000a010);
331
Dave Chapmand8c5d1b2005-11-08 09:07:13 +0000332 /* set port B03 on */
Dave Chapman38e8fb62005-11-08 00:52:39 +0000333 outl(((0x100 | 1) << 3), 0x6000d824);
334
Dave Chapman8b1297a2006-02-21 15:01:25 +0000335#elif CONFIG_BACKLIGHT==BL_IPODMINI
336 /* set port B03 on */
337 outl(((0x100 | 1) << 3), 0x6000d824);
338
Dave Chapman38e8fb62005-11-08 00:52:39 +0000339#elif CONFIG_BACKLIGHT==BL_IPODNANO
340
341 /* set port B03 on */
342 outl(((0x100 | 1) << 3), 0x6000d824);
343
344 /* set port L07 on */
345 outl(((0x100 | 1) << 7), 0x6000d12c);
Dave Chapmanfa743562006-02-05 19:05:55 +0000346#elif CONFIG_BACKLIGHT==BL_IPOD3G
347 outl(inl(IPOD_LCD_BASE) | 0x2, IPOD_LCD_BASE);
Dave Chapman38e8fb62005-11-08 00:52:39 +0000348#endif
349
Dave Chapmanf9dac402006-01-31 01:50:07 +0000350 TMP_IPOD_HW_REVISION = IPOD_HW_REVISION;
Dave Chapman1446b212006-01-31 09:40:21 +0000351 ipod_hw_rev = IPOD_HW_REVISION;
Dave Chapmanf9dac402006-01-31 01:50:07 +0000352
Dave Chapman38e8fb62005-11-08 00:52:39 +0000353 system_init();
354 kernel_init();
355 lcd_init();
356 font_init();
357
358#if 0
359 /* ADC and button drivers are not yet implemented */
360 adc_init();
361 button_init();
362#endif
363
Dave Chapman3ded3ce2006-01-18 19:52:02 +0000364 line=0;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000365
366 lcd_setfont(FONT_SYSFIXED);
367
Dave Chapman3ded3ce2006-01-18 19:52:02 +0000368 lcd_puts(0, line++, "Rockbox boot loader");
Dave Chapman38e8fb62005-11-08 00:52:39 +0000369 snprintf(buf, sizeof(buf), "Version: 20%s", version);
370 lcd_puts(0, line++, buf);
371 snprintf(buf, sizeof(buf), "IPOD version: 0x%08x", IPOD_HW_REVISION);
372 lcd_puts(0, line++, buf);
373 lcd_update();
374
375 i=ata_init();
376 if (i==0) {
377 identify_info=ata_get_identify();
378 /* Show model */
379 for (i=0; i < 20; i++) {
380 ((unsigned short*)buf)[i]=htobe16(identify_info[i+27]);
381 }
382 buf[40]=0;
383 for (i=39; i && buf[i]==' '; i--) {
384 buf[i]=0;
385 }
386 lcd_puts(0, line++, buf);
387 lcd_update();
388 } else {
389 snprintf(buf, sizeof(buf), "ATA: %d", i);
390 lcd_puts(0, line++, buf);
391 lcd_update();
392 }
393
394 disk_init();
395 rc = disk_mount_all();
396 if (rc<=0)
397 {
398 lcd_puts(0, line++, "No partition found");
399 lcd_update();
400// while(button_get(true) != SYS_USB_CONNECTED) {};
401 }
402
403 pinfo = disk_partinfo(1);
404 snprintf(buf, sizeof(buf), "Partition 1: 0x%02x %ld MB",
405 pinfo->type, pinfo->size / 2048);
406 lcd_puts(0, line++, buf);
407 lcd_update();
408
Dave Chapman061f3802005-11-13 20:59:30 +0000409 /* Check for a keypress */
410 i=key_pressed();
Dave Chapman38e8fb62005-11-08 00:52:39 +0000411
Dave Chapman20eed882005-11-20 10:57:53 +0000412 if ((i!=BUTTON_MENU) && (i!=BUTTON_PLAY)) {
Dave Chapman061f3802005-11-13 20:59:30 +0000413 lcd_puts(0, line, "Loading Rockbox...");
414 lcd_update();
415 rc=load_rockbox(loadbuffer);
416 if (rc < 0) {
417 snprintf(buf, sizeof(buf), "Rockbox error: %d",rc);
418 lcd_puts(0, line++, buf);
419 lcd_update();
420 } else {
421 lcd_puts(0, line++, "Rockbox loaded.");
422 lcd_update();
Dave Chapman061f3802005-11-13 20:59:30 +0000423 memcpy((void*)DRAM_START,loadbuffer,rc);
Dave Chapmancb7e6952006-01-05 17:02:48 +0000424
425 /* Transfer execution directly to Rockbox - we don't want
426 to run the rest of the bootloader startup code. */
427 asm volatile(
Dave Chapman8adc81d2006-02-21 21:13:03 +0000428 "mov r0, #" SC(DRAM_START) "\n"
429 "mov pc, r0 \n"
Dave Chapmancb7e6952006-01-05 17:02:48 +0000430 );
431
432 /* We don't get here, but keep the compiler happy. */
433 return (void*)0;
Dave Chapman061f3802005-11-13 20:59:30 +0000434 }
435 }
436
437 if (i==BUTTON_PLAY) {
438 lcd_puts(0, line, "Loading Linux...");
439 lcd_update();
440 rc=load_linux(loadbuffer);
441 if (rc < 0) {
442 snprintf(buf, sizeof(buf), "Linux error: %d",rc);
443 lcd_puts(0, line++, buf);
444 lcd_update();
445 } else {
446 memcpy((void*)DRAM_START,loadbuffer,rc);
447 return (void*)DRAM_START;
448 }
449 }
Dave Chapman38e8fb62005-11-08 00:52:39 +0000450
451 /* If everything else failed, try the original firmware */
452 lcd_puts(0, line, "Loading original firmware...");
453 lcd_update();
454
Dave Chapman061f3802005-11-13 20:59:30 +0000455 /* Pause for 5 seconds so we can see what's happened */
Dave Chapmanfa743562006-02-05 19:05:55 +0000456// udelay(5000000);
Dave Chapman061f3802005-11-13 20:59:30 +0000457
Dave Chapman38e8fb62005-11-08 00:52:39 +0000458 entry = tblp->addr + tblp->entryOffset;
459 if (imageno || ((int)tblp->addr & 0xffffff) != 0) {
460 memmove16(tblp->addr, tblp->addr + tblp->devOffset - padding,
461 tblp->len);
462 }
463
464 /* Return the start address in loaded image */
465 return entry;
466}
467
468/* These functions are present in the firmware library, but we reimplement
469 them here because the originals do a lot more than we want */
470
471void reset_poweroff_timer(void)
472{
473}
474
Dave Chapman38e8fb62005-11-08 00:52:39 +0000475int dbg_ports(void)
476{
477 return 0;
478}
479
480void mpeg_stop(void)
481{
482}
483
484void usb_acknowledge(void)
485{
486}
487
488void usb_wait_for_disconnect(void)
489{
490}
491
492void sys_poweroff(void)
493{
494}