blob: 63b7b2207b22246010517f7f79d59d05fbe2cf3e [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 Chapmanfa743562006-02-05 19:05:55 +000042#if (CONFIG_CPU == PP5020)
43#define DRAM_START 0x10000000
44#else
45#define IPOD_LCD_BASE 0xc0001000
46#define DRAM_START 0x28000000
47#endif
Dave Chapman38e8fb62005-11-08 00:52:39 +000048#define IPOD_HW_REVISION (*((volatile unsigned long*)(0x00002084)))
49
Dave Chapmanf9dac402006-01-31 01:50:07 +000050/* We copy the hardware revision to the last four bytes of SDRAM and then
51 re-read it after we have re-mapped SDRAM to 0x0 in Rockbox */
52#define TMP_IPOD_HW_REVISION (*((volatile unsigned long*)(0x11fffffc)))
53
Dave Chapman061f3802005-11-13 20:59:30 +000054#define BUTTON_LEFT 1
55#define BUTTON_MENU 2
56#define BUTTON_RIGHT 3
57#define BUTTON_PLAY 4
Dave Chapmanfa743562006-02-05 19:05:55 +000058#define BUTTON_HOLD 5
Dave Chapman061f3802005-11-13 20:59:30 +000059
60/* Size of the buffer to store the loaded Rockbox/Linux image */
61#define MAX_LOADSIZE (4*1024*1024)
62
Dave Chapman38e8fb62005-11-08 00:52:39 +000063char version[] = APPSVERSION;
64
Dave Chapman38e8fb62005-11-08 00:52:39 +000065typedef struct _image {
66 unsigned type; /* '' */
67 unsigned id; /* */
68 unsigned pad1; /* 0000 0000 */
69 unsigned devOffset; /* byte offset of start of image code */
70 unsigned len; /* length in bytes of image */
71 void *addr; /* load address */
72 unsigned entryOffset; /* execution start within image */
73 unsigned chksum; /* checksum for image */
74 unsigned vers; /* image version */
75 unsigned loadAddr; /* load address for image */
76} image_t;
77
78extern image_t boot_table[];
79
80int line=0;
81
82static void memmove16(void *dest, const void *src, unsigned count)
83{
84 struct bufstr {
85 unsigned _buf[4];
86 } *d, *s;
87
88 if (src >= dest) {
89 count = (count + 15) >> 4;
90 d = (struct bufstr *) dest;
91 s = (struct bufstr *) src;
92 while (count--)
93 *d++ = *s++;
94 } else {
95 count = (count + 15) >> 4;
96 d = (struct bufstr *)(dest + (count <<4));
97 s = (struct bufstr *)(src + (count <<4));
98 while (count--)
99 *--d = *--s;
100 }
101}
102
Dave Chapmanfa743562006-02-05 19:05:55 +0000103#if CONFIG_KEYPAD == IPOD_4G_PAD
Dave Chapman38e8fb62005-11-08 00:52:39 +0000104/* check if number of seconds has past */
105int timer_check(int clock_start, unsigned int usecs)
106{
Dave Chapmanfa743562006-02-05 19:05:55 +0000107 if ((USEC_TIMER - clock_start) >= usecs) {
Dave Chapman38e8fb62005-11-08 00:52:39 +0000108 return 1;
109 } else {
110 return 0;
111 }
112}
113
Dave Chapman061f3802005-11-13 20:59:30 +0000114static void ser_opto_keypad_cfg(int val)
115{
116 int start_time;
117
118 outl(inl(0x6000d004) & ~0x80, 0x6000d004);
119
120 outl(inl(0x7000c104) | 0xc000000, 0x7000c104);
121 outl(val, 0x7000c120);
122 outl(inl(0x7000c100) | 0x80000000, 0x7000c100);
123
124 outl(inl(0x6000d024) & ~0x10, 0x6000d024);
125 outl(inl(0x6000d014) | 0x10, 0x6000d014);
126
Dave Chapmanfa743562006-02-05 19:05:55 +0000127 start_time = USEC_TIMER;
Dave Chapman061f3802005-11-13 20:59:30 +0000128 do {
129 if ((inl(0x7000c104) & 0x80000000) == 0) {
130 break;
131 }
132 } while (timer_check(start_time, 1500) != 0);
133
134 outl(inl(0x7000c100) & ~0x80000000, 0x7000c100);
135
136 outl(inl(0x6000d004) | 0x80, 0x6000d004);
137 outl(inl(0x6000d024) | 0x10, 0x6000d024);
138 outl(inl(0x6000d014) & ~0x10, 0x6000d014);
139
140 outl(inl(0x7000c104) | 0xc000000, 0x7000c104);
141 outl(inl(0x7000c100) | 0x60000000, 0x7000c100);
142}
143
144int opto_keypad_read(void)
145{
146 int loop_cnt, had_io = 0;
147
148 for (loop_cnt = 5; loop_cnt != 0;)
149 {
150 int key_pressed = 0;
151 int start_time;
152 unsigned int key_pad_val;
153
154 ser_opto_keypad_cfg(0x8000023a);
155
Dave Chapmanfa743562006-02-05 19:05:55 +0000156 start_time = USEC_TIMER;
Dave Chapman061f3802005-11-13 20:59:30 +0000157 do {
158 if (inl(0x7000c104) & 0x4000000) {
159 had_io = 1;
160 break;
161 }
162
163 if (had_io != 0) {
164 break;
165 }
166 } while (timer_check(start_time, 1500) != 0);
167
168 key_pad_val = inl(0x7000c140);
169 if ((key_pad_val & ~0x7fff0000) != 0x8000023a) {
170 loop_cnt--;
171 } else {
172 key_pad_val = (key_pad_val << 11) >> 27;
173 key_pressed = 1;
174 }
175
176 outl(inl(0x7000c100) | 0x60000000, 0x7000c100);
177 outl(inl(0x7000c104) | 0xc000000, 0x7000c104);
178
179 if (key_pressed != 0) {
180 return key_pad_val ^ 0x1f;
181 }
182 }
183
184 return 0;
185}
Dave Chapmanfa743562006-02-05 19:05:55 +0000186#endif
Dave Chapman061f3802005-11-13 20:59:30 +0000187
188static int key_pressed(void)
189{
190 unsigned char state;
191
Dave Chapmanfa743562006-02-05 19:05:55 +0000192#if CONFIG_KEYPAD == IPOD_4G_PAD
Dave Chapman061f3802005-11-13 20:59:30 +0000193 state = opto_keypad_read();
194 if ((state & 0x4) == 0) return BUTTON_LEFT;
195 if ((state & 0x10) == 0) return BUTTON_MENU;
196 if ((state & 0x8) == 0) return BUTTON_PLAY;
197 if ((state & 0x2) == 0) return BUTTON_RIGHT;
Dave Chapmanfa743562006-02-05 19:05:55 +0000198#elif CONFIG_KEYPAD == IPOD_3G_PAD
199 state = inb(0xcf000030);
200 if (((state & 0x20) == 0)) return BUTTON_HOLD; /* hold on */
201 if ((state & 0x08) == 0) return BUTTON_LEFT;
202 if ((state & 0x10) == 0) return BUTTON_MENU;
203 if ((state & 0x04) == 0) return BUTTON_PLAY;
204 if ((state & 0x01) == 0) return BUTTON_RIGHT;
205#endif
Dave Chapman061f3802005-11-13 20:59:30 +0000206 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);
Dave Chapmanfa743562006-02-05 19:05:55 +0000335#elif CONFIG_BACKLIGHT==BL_IPOD3G
336 outl(inl(IPOD_LCD_BASE) | 0x2, IPOD_LCD_BASE);
Dave Chapman38e8fb62005-11-08 00:52:39 +0000337#endif
338
Dave Chapmanf9dac402006-01-31 01:50:07 +0000339 TMP_IPOD_HW_REVISION = IPOD_HW_REVISION;
Dave Chapman1446b212006-01-31 09:40:21 +0000340 ipod_hw_rev = IPOD_HW_REVISION;
Dave Chapmanf9dac402006-01-31 01:50:07 +0000341
Dave Chapman38e8fb62005-11-08 00:52:39 +0000342 system_init();
343 kernel_init();
344 lcd_init();
345 font_init();
346
347#if 0
348 /* ADC and button drivers are not yet implemented */
349 adc_init();
350 button_init();
351#endif
352
Dave Chapman3ded3ce2006-01-18 19:52:02 +0000353 line=0;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000354
355 lcd_setfont(FONT_SYSFIXED);
356
Dave Chapman3ded3ce2006-01-18 19:52:02 +0000357 lcd_puts(0, line++, "Rockbox boot loader");
Dave Chapman38e8fb62005-11-08 00:52:39 +0000358 snprintf(buf, sizeof(buf), "Version: 20%s", version);
359 lcd_puts(0, line++, buf);
360 snprintf(buf, sizeof(buf), "IPOD version: 0x%08x", IPOD_HW_REVISION);
361 lcd_puts(0, line++, buf);
362 lcd_update();
363
364 i=ata_init();
365 if (i==0) {
366 identify_info=ata_get_identify();
367 /* Show model */
368 for (i=0; i < 20; i++) {
369 ((unsigned short*)buf)[i]=htobe16(identify_info[i+27]);
370 }
371 buf[40]=0;
372 for (i=39; i && buf[i]==' '; i--) {
373 buf[i]=0;
374 }
375 lcd_puts(0, line++, buf);
376 lcd_update();
377 } else {
378 snprintf(buf, sizeof(buf), "ATA: %d", i);
379 lcd_puts(0, line++, buf);
380 lcd_update();
381 }
382
383 disk_init();
384 rc = disk_mount_all();
385 if (rc<=0)
386 {
387 lcd_puts(0, line++, "No partition found");
388 lcd_update();
389// while(button_get(true) != SYS_USB_CONNECTED) {};
390 }
391
392 pinfo = disk_partinfo(1);
393 snprintf(buf, sizeof(buf), "Partition 1: 0x%02x %ld MB",
394 pinfo->type, pinfo->size / 2048);
395 lcd_puts(0, line++, buf);
396 lcd_update();
397
Dave Chapman061f3802005-11-13 20:59:30 +0000398 /* Check for a keypress */
399 i=key_pressed();
Dave Chapman38e8fb62005-11-08 00:52:39 +0000400
Dave Chapman20eed882005-11-20 10:57:53 +0000401 if ((i!=BUTTON_MENU) && (i!=BUTTON_PLAY)) {
Dave Chapman061f3802005-11-13 20:59:30 +0000402 lcd_puts(0, line, "Loading Rockbox...");
403 lcd_update();
404 rc=load_rockbox(loadbuffer);
405 if (rc < 0) {
406 snprintf(buf, sizeof(buf), "Rockbox error: %d",rc);
407 lcd_puts(0, line++, buf);
408 lcd_update();
409 } else {
410 lcd_puts(0, line++, "Rockbox loaded.");
411 lcd_update();
Dave Chapman061f3802005-11-13 20:59:30 +0000412 memcpy((void*)DRAM_START,loadbuffer,rc);
Dave Chapmancb7e6952006-01-05 17:02:48 +0000413
414 /* Transfer execution directly to Rockbox - we don't want
415 to run the rest of the bootloader startup code. */
416 asm volatile(
417 "mov r0, #0x10000000 \n"
418 "mov pc, r0 \n"
419 );
420
421 /* We don't get here, but keep the compiler happy. */
422 return (void*)0;
Dave Chapman061f3802005-11-13 20:59:30 +0000423 }
424 }
425
426 if (i==BUTTON_PLAY) {
427 lcd_puts(0, line, "Loading Linux...");
428 lcd_update();
429 rc=load_linux(loadbuffer);
430 if (rc < 0) {
431 snprintf(buf, sizeof(buf), "Linux error: %d",rc);
432 lcd_puts(0, line++, buf);
433 lcd_update();
434 } else {
435 memcpy((void*)DRAM_START,loadbuffer,rc);
436 return (void*)DRAM_START;
437 }
438 }
Dave Chapman38e8fb62005-11-08 00:52:39 +0000439
440 /* If everything else failed, try the original firmware */
441 lcd_puts(0, line, "Loading original firmware...");
442 lcd_update();
443
Dave Chapman061f3802005-11-13 20:59:30 +0000444 /* Pause for 5 seconds so we can see what's happened */
Dave Chapmanfa743562006-02-05 19:05:55 +0000445// udelay(5000000);
Dave Chapman061f3802005-11-13 20:59:30 +0000446
Dave Chapman38e8fb62005-11-08 00:52:39 +0000447 entry = tblp->addr + tblp->entryOffset;
448 if (imageno || ((int)tblp->addr & 0xffffff) != 0) {
449 memmove16(tblp->addr, tblp->addr + tblp->devOffset - padding,
450 tblp->len);
451 }
452
453 /* Return the start address in loaded image */
454 return entry;
455}
456
457/* These functions are present in the firmware library, but we reimplement
458 them here because the originals do a lot more than we want */
459
460void reset_poweroff_timer(void)
461{
462}
463
Dave Chapman38e8fb62005-11-08 00:52:39 +0000464int dbg_ports(void)
465{
466 return 0;
467}
468
469void mpeg_stop(void)
470{
471}
472
473void usb_acknowledge(void)
474{
475}
476
477void usb_wait_for_disconnect(void)
478{
479}
480
481void sys_poweroff(void)
482{
483}