blob: f62fa265a5c9bd3249cf6133be83ce728f488ef1 [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 Chapman061f3802005-11-13 20:59:30 +0000196 state = opto_keypad_read();
197 if ((state & 0x4) == 0) return BUTTON_LEFT;
198 if ((state & 0x10) == 0) return BUTTON_MENU;
199 if ((state & 0x8) == 0) return BUTTON_PLAY;
200 if ((state & 0x2) == 0) return BUTTON_RIGHT;
Dave Chapmanfa743562006-02-05 19:05:55 +0000201#elif CONFIG_KEYPAD == IPOD_3G_PAD
202 state = inb(0xcf000030);
203 if (((state & 0x20) == 0)) return BUTTON_HOLD; /* hold on */
204 if ((state & 0x08) == 0) return BUTTON_LEFT;
205 if ((state & 0x10) == 0) return BUTTON_MENU;
206 if ((state & 0x04) == 0) return BUTTON_PLAY;
207 if ((state & 0x01) == 0) return BUTTON_RIGHT;
208#endif
Dave Chapman061f3802005-11-13 20:59:30 +0000209 return 0;
210}
211
212int load_rockbox(unsigned char* buf)
Dave Chapman38e8fb62005-11-08 00:52:39 +0000213{
214 int fd;
215 int rc;
216 int len;
217 unsigned long chksum;
218 char model[5];
219 unsigned long sum;
220 int i;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000221 char str[80];
222
Dave Chapman20eed882005-11-20 10:57:53 +0000223 fd = open("/.rockbox/" BOOTFILE, O_RDONLY);
Dave Chapman38e8fb62005-11-08 00:52:39 +0000224 if(fd < 0)
Dave Chapman20eed882005-11-20 10:57:53 +0000225 {
226 fd = open("/" BOOTFILE, O_RDONLY);
227 if(fd < 0)
228 return -1;
229 }
Dave Chapman38e8fb62005-11-08 00:52:39 +0000230
231 len = filesize(fd) - 8;
232
Dave Chapman061f3802005-11-13 20:59:30 +0000233 if (len > MAX_LOADSIZE)
234 return -6;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000235
236 lseek(fd, FIRMWARE_OFFSET_FILE_CRC, SEEK_SET);
237
238 rc = read(fd, &chksum, 4);
Dave Chapman061f3802005-11-13 20:59:30 +0000239 chksum=betoh32(chksum); /* Rockbox checksums are big-endian */
Dave Chapman38e8fb62005-11-08 00:52:39 +0000240 if(rc < 4)
Dave Chapman061f3802005-11-13 20:59:30 +0000241 return -2;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000242
243 rc = read(fd, model, 4);
244 if(rc < 4)
Dave Chapman061f3802005-11-13 20:59:30 +0000245 return -3;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000246
247 model[4] = 0;
248
Thom Johansen1c69caf2006-01-18 23:02:00 +0000249 snprintf(str, 80, "Model: %s", model);
250 lcd_puts(0, line++, str);
251 snprintf(str, 80, "Checksum: %x", chksum);
Dave Chapman38e8fb62005-11-08 00:52:39 +0000252 lcd_puts(0, line++, str);
253 lcd_update();
254
255 lseek(fd, FIRMWARE_OFFSET_FILE_DATA, SEEK_SET);
256
257 rc = read(fd, buf, len);
258 if(rc < len)
Dave Chapman061f3802005-11-13 20:59:30 +0000259 return -4;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000260
261 close(fd);
262
263 sum = MODEL_NUMBER;
264
265 for(i = 0;i < len;i++) {
Dave Chapman061f3802005-11-13 20:59:30 +0000266 sum += buf[i];
Dave Chapman38e8fb62005-11-08 00:52:39 +0000267 }
268
269 snprintf(str, 80, "Sum: %x", sum);
270 lcd_puts(0, line++, str);
271 lcd_update();
272
273 if(sum != chksum)
274 return -5;
275
Dave Chapman061f3802005-11-13 20:59:30 +0000276 return len;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000277}
278
Dave Chapman061f3802005-11-13 20:59:30 +0000279
280int load_linux(unsigned char* buf) {
281 int fd;
282 int rc;
283 int len;
284 char str[80];
285
286 fd=open("/linux.bin",O_RDONLY);
287 if (fd < 0)
288 return -1;
289
290 len=filesize(fd);
291 if (len > MAX_LOADSIZE)
292 return -6;
293
294 rc=read(fd,buf,len);
295
296 if (rc < len)
297 return -4;
298
299 snprintf(str, 80, "Loaded Linux: %d bytes", len);
300 lcd_puts(0, line++, str);
301 lcd_update();
302
303 return len;
304}
305
306
307/* A buffer to load the Linux kernel or Rockbox into */
308unsigned char loadbuffer[MAX_LOADSIZE];
309
Dave Chapman38e8fb62005-11-08 00:52:39 +0000310void* main(void)
311{
312 char buf[256];
313 int imageno=0;
314 int i;
315 int rc;
316 int padding = 0x4400;
317 image_t *tblp = boot_table;
318 void* entry;
319 struct partinfo* pinfo;
320 unsigned short* identify_info;
321
322 /* Turn on the backlight */
323
324#if CONFIG_BACKLIGHT==BL_IPOD4G
Dave Chapman38e8fb62005-11-08 00:52:39 +0000325 /* brightness full */
326 outl(0x80000000 | (0xff << 16), 0x7000a010);
327
Dave Chapmand8c5d1b2005-11-08 09:07:13 +0000328 /* set port B03 on */
Dave Chapman38e8fb62005-11-08 00:52:39 +0000329 outl(((0x100 | 1) << 3), 0x6000d824);
330
Dave Chapman8b1297a2006-02-21 15:01:25 +0000331#elif CONFIG_BACKLIGHT==BL_IPODMINI
332 /* set port B03 on */
333 outl(((0x100 | 1) << 3), 0x6000d824);
334
Dave Chapman38e8fb62005-11-08 00:52:39 +0000335#elif CONFIG_BACKLIGHT==BL_IPODNANO
336
337 /* set port B03 on */
338 outl(((0x100 | 1) << 3), 0x6000d824);
339
340 /* set port L07 on */
341 outl(((0x100 | 1) << 7), 0x6000d12c);
Dave Chapmanfa743562006-02-05 19:05:55 +0000342#elif CONFIG_BACKLIGHT==BL_IPOD3G
343 outl(inl(IPOD_LCD_BASE) | 0x2, IPOD_LCD_BASE);
Dave Chapman38e8fb62005-11-08 00:52:39 +0000344#endif
345
Dave Chapmanf9dac402006-01-31 01:50:07 +0000346 TMP_IPOD_HW_REVISION = IPOD_HW_REVISION;
Dave Chapman1446b212006-01-31 09:40:21 +0000347 ipod_hw_rev = IPOD_HW_REVISION;
Dave Chapmanf9dac402006-01-31 01:50:07 +0000348
Dave Chapman38e8fb62005-11-08 00:52:39 +0000349 system_init();
350 kernel_init();
351 lcd_init();
352 font_init();
353
354#if 0
355 /* ADC and button drivers are not yet implemented */
356 adc_init();
357 button_init();
358#endif
359
Dave Chapman3ded3ce2006-01-18 19:52:02 +0000360 line=0;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000361
362 lcd_setfont(FONT_SYSFIXED);
363
Dave Chapman3ded3ce2006-01-18 19:52:02 +0000364 lcd_puts(0, line++, "Rockbox boot loader");
Dave Chapman38e8fb62005-11-08 00:52:39 +0000365 snprintf(buf, sizeof(buf), "Version: 20%s", version);
366 lcd_puts(0, line++, buf);
367 snprintf(buf, sizeof(buf), "IPOD version: 0x%08x", IPOD_HW_REVISION);
368 lcd_puts(0, line++, buf);
369 lcd_update();
370
371 i=ata_init();
372 if (i==0) {
373 identify_info=ata_get_identify();
374 /* Show model */
375 for (i=0; i < 20; i++) {
376 ((unsigned short*)buf)[i]=htobe16(identify_info[i+27]);
377 }
378 buf[40]=0;
379 for (i=39; i && buf[i]==' '; i--) {
380 buf[i]=0;
381 }
382 lcd_puts(0, line++, buf);
383 lcd_update();
384 } else {
385 snprintf(buf, sizeof(buf), "ATA: %d", i);
386 lcd_puts(0, line++, buf);
387 lcd_update();
388 }
389
390 disk_init();
391 rc = disk_mount_all();
392 if (rc<=0)
393 {
394 lcd_puts(0, line++, "No partition found");
395 lcd_update();
396// while(button_get(true) != SYS_USB_CONNECTED) {};
397 }
398
399 pinfo = disk_partinfo(1);
400 snprintf(buf, sizeof(buf), "Partition 1: 0x%02x %ld MB",
401 pinfo->type, pinfo->size / 2048);
402 lcd_puts(0, line++, buf);
403 lcd_update();
404
Dave Chapman061f3802005-11-13 20:59:30 +0000405 /* Check for a keypress */
406 i=key_pressed();
Dave Chapman38e8fb62005-11-08 00:52:39 +0000407
Dave Chapman20eed882005-11-20 10:57:53 +0000408 if ((i!=BUTTON_MENU) && (i!=BUTTON_PLAY)) {
Dave Chapman061f3802005-11-13 20:59:30 +0000409 lcd_puts(0, line, "Loading Rockbox...");
410 lcd_update();
411 rc=load_rockbox(loadbuffer);
412 if (rc < 0) {
413 snprintf(buf, sizeof(buf), "Rockbox error: %d",rc);
414 lcd_puts(0, line++, buf);
415 lcd_update();
416 } else {
417 lcd_puts(0, line++, "Rockbox loaded.");
418 lcd_update();
Dave Chapman061f3802005-11-13 20:59:30 +0000419 memcpy((void*)DRAM_START,loadbuffer,rc);
Dave Chapmancb7e6952006-01-05 17:02:48 +0000420
421 /* Transfer execution directly to Rockbox - we don't want
422 to run the rest of the bootloader startup code. */
423 asm volatile(
Dave Chapman8adc81d2006-02-21 21:13:03 +0000424 "mov r0, #" SC(DRAM_START) "\n"
425 "mov pc, r0 \n"
Dave Chapmancb7e6952006-01-05 17:02:48 +0000426 );
427
428 /* We don't get here, but keep the compiler happy. */
429 return (void*)0;
Dave Chapman061f3802005-11-13 20:59:30 +0000430 }
431 }
432
433 if (i==BUTTON_PLAY) {
434 lcd_puts(0, line, "Loading Linux...");
435 lcd_update();
436 rc=load_linux(loadbuffer);
437 if (rc < 0) {
438 snprintf(buf, sizeof(buf), "Linux error: %d",rc);
439 lcd_puts(0, line++, buf);
440 lcd_update();
441 } else {
442 memcpy((void*)DRAM_START,loadbuffer,rc);
443 return (void*)DRAM_START;
444 }
445 }
Dave Chapman38e8fb62005-11-08 00:52:39 +0000446
447 /* If everything else failed, try the original firmware */
448 lcd_puts(0, line, "Loading original firmware...");
449 lcd_update();
450
Dave Chapman061f3802005-11-13 20:59:30 +0000451 /* Pause for 5 seconds so we can see what's happened */
Dave Chapmanfa743562006-02-05 19:05:55 +0000452// udelay(5000000);
Dave Chapman061f3802005-11-13 20:59:30 +0000453
Dave Chapman38e8fb62005-11-08 00:52:39 +0000454 entry = tblp->addr + tblp->entryOffset;
455 if (imageno || ((int)tblp->addr & 0xffffff) != 0) {
456 memmove16(tblp->addr, tblp->addr + tblp->devOffset - padding,
457 tblp->len);
458 }
459
460 /* Return the start address in loaded image */
461 return entry;
462}
463
464/* These functions are present in the firmware library, but we reimplement
465 them here because the originals do a lot more than we want */
466
467void reset_poweroff_timer(void)
468{
469}
470
Dave Chapman38e8fb62005-11-08 00:52:39 +0000471int dbg_ports(void)
472{
473 return 0;
474}
475
476void mpeg_stop(void)
477{
478}
479
480void usb_acknowledge(void)
481{
482}
483
484void usb_wait_for_disconnect(void)
485{
486}
487
488void sys_poweroff(void)
489{
490}