blob: beb84e3addf4395ae8afe5ba0a8e1556515a9295 [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
Dave Chapman061f3802005-11-13 20:59:30 +0000246 snprintf(str, 80, "Model: %s, Checksum: %x", model, chksum);
Dave Chapman38e8fb62005-11-08 00:52:39 +0000247 lcd_puts(0, line++, str);
248 lcd_update();
249
250 lseek(fd, FIRMWARE_OFFSET_FILE_DATA, SEEK_SET);
251
252 rc = read(fd, buf, len);
253 if(rc < len)
Dave Chapman061f3802005-11-13 20:59:30 +0000254 return -4;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000255
256 close(fd);
257
258 sum = MODEL_NUMBER;
259
260 for(i = 0;i < len;i++) {
Dave Chapman061f3802005-11-13 20:59:30 +0000261 sum += buf[i];
Dave Chapman38e8fb62005-11-08 00:52:39 +0000262 }
263
264 snprintf(str, 80, "Sum: %x", sum);
265 lcd_puts(0, line++, str);
266 lcd_update();
267
268 if(sum != chksum)
269 return -5;
270
Dave Chapman061f3802005-11-13 20:59:30 +0000271 return len;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000272}
273
Dave Chapman061f3802005-11-13 20:59:30 +0000274
275int load_linux(unsigned char* buf) {
276 int fd;
277 int rc;
278 int len;
279 char str[80];
280
281 fd=open("/linux.bin",O_RDONLY);
282 if (fd < 0)
283 return -1;
284
285 len=filesize(fd);
286 if (len > MAX_LOADSIZE)
287 return -6;
288
289 rc=read(fd,buf,len);
290
291 if (rc < len)
292 return -4;
293
294 snprintf(str, 80, "Loaded Linux: %d bytes", len);
295 lcd_puts(0, line++, str);
296 lcd_update();
297
298 return len;
299}
300
301
302/* A buffer to load the Linux kernel or Rockbox into */
303unsigned char loadbuffer[MAX_LOADSIZE];
304
Dave Chapman38e8fb62005-11-08 00:52:39 +0000305void* main(void)
306{
307 char buf[256];
308 int imageno=0;
309 int i;
310 int rc;
311 int padding = 0x4400;
312 image_t *tblp = boot_table;
313 void* entry;
314 struct partinfo* pinfo;
315 unsigned short* identify_info;
316
317 /* Turn on the backlight */
318
319#if CONFIG_BACKLIGHT==BL_IPOD4G
Dave Chapman38e8fb62005-11-08 00:52:39 +0000320 /* brightness full */
321 outl(0x80000000 | (0xff << 16), 0x7000a010);
322
Dave Chapmand8c5d1b2005-11-08 09:07:13 +0000323 /* set port B03 on */
Dave Chapman38e8fb62005-11-08 00:52:39 +0000324 outl(((0x100 | 1) << 3), 0x6000d824);
325
326#elif CONFIG_BACKLIGHT==BL_IPODNANO
327
328 /* set port B03 on */
329 outl(((0x100 | 1) << 3), 0x6000d824);
330
331 /* set port L07 on */
332 outl(((0x100 | 1) << 7), 0x6000d12c);
333
334#endif
335
336 system_init();
337 kernel_init();
338 lcd_init();
339 font_init();
340
341#if 0
342 /* ADC and button drivers are not yet implemented */
343 adc_init();
344 button_init();
345#endif
346
Dave Chapman3ded3ce2006-01-18 19:52:02 +0000347 line=0;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000348
349 lcd_setfont(FONT_SYSFIXED);
350
Dave Chapman3ded3ce2006-01-18 19:52:02 +0000351 lcd_puts(0, line++, "Rockbox boot loader");
Dave Chapman38e8fb62005-11-08 00:52:39 +0000352 snprintf(buf, sizeof(buf), "Version: 20%s", version);
353 lcd_puts(0, line++, buf);
354 snprintf(buf, sizeof(buf), "IPOD version: 0x%08x", IPOD_HW_REVISION);
355 lcd_puts(0, line++, buf);
356 lcd_update();
357
358 i=ata_init();
359 if (i==0) {
360 identify_info=ata_get_identify();
361 /* Show model */
362 for (i=0; i < 20; i++) {
363 ((unsigned short*)buf)[i]=htobe16(identify_info[i+27]);
364 }
365 buf[40]=0;
366 for (i=39; i && buf[i]==' '; i--) {
367 buf[i]=0;
368 }
369 lcd_puts(0, line++, buf);
370 lcd_update();
371 } else {
372 snprintf(buf, sizeof(buf), "ATA: %d", i);
373 lcd_puts(0, line++, buf);
374 lcd_update();
375 }
376
377 disk_init();
378 rc = disk_mount_all();
379 if (rc<=0)
380 {
381 lcd_puts(0, line++, "No partition found");
382 lcd_update();
383// while(button_get(true) != SYS_USB_CONNECTED) {};
384 }
385
386 pinfo = disk_partinfo(1);
387 snprintf(buf, sizeof(buf), "Partition 1: 0x%02x %ld MB",
388 pinfo->type, pinfo->size / 2048);
389 lcd_puts(0, line++, buf);
390 lcd_update();
391
Dave Chapman061f3802005-11-13 20:59:30 +0000392 /* Check for a keypress */
393 i=key_pressed();
Dave Chapman38e8fb62005-11-08 00:52:39 +0000394
Dave Chapman20eed882005-11-20 10:57:53 +0000395 if ((i!=BUTTON_MENU) && (i!=BUTTON_PLAY)) {
Dave Chapman061f3802005-11-13 20:59:30 +0000396 lcd_puts(0, line, "Loading Rockbox...");
397 lcd_update();
398 rc=load_rockbox(loadbuffer);
399 if (rc < 0) {
400 snprintf(buf, sizeof(buf), "Rockbox error: %d",rc);
401 lcd_puts(0, line++, buf);
402 lcd_update();
403 } else {
404 lcd_puts(0, line++, "Rockbox loaded.");
405 lcd_update();
Dave Chapman061f3802005-11-13 20:59:30 +0000406 memcpy((void*)DRAM_START,loadbuffer,rc);
Dave Chapmancb7e6952006-01-05 17:02:48 +0000407
408 /* Transfer execution directly to Rockbox - we don't want
409 to run the rest of the bootloader startup code. */
410 asm volatile(
411 "mov r0, #0x10000000 \n"
412 "mov pc, r0 \n"
413 );
414
415 /* We don't get here, but keep the compiler happy. */
416 return (void*)0;
Dave Chapman061f3802005-11-13 20:59:30 +0000417 }
418 }
419
420 if (i==BUTTON_PLAY) {
421 lcd_puts(0, line, "Loading Linux...");
422 lcd_update();
423 rc=load_linux(loadbuffer);
424 if (rc < 0) {
425 snprintf(buf, sizeof(buf), "Linux error: %d",rc);
426 lcd_puts(0, line++, buf);
427 lcd_update();
428 } else {
429 memcpy((void*)DRAM_START,loadbuffer,rc);
430 return (void*)DRAM_START;
431 }
432 }
Dave Chapman38e8fb62005-11-08 00:52:39 +0000433
434 /* If everything else failed, try the original firmware */
Dave Chapman061f3802005-11-13 20:59:30 +0000435
Dave Chapman38e8fb62005-11-08 00:52:39 +0000436 lcd_puts(0, line, "Loading original firmware...");
437 lcd_update();
438
Dave Chapman061f3802005-11-13 20:59:30 +0000439 /* Pause for 5 seconds so we can see what's happened */
Dave Chapman20eed882005-11-20 10:57:53 +0000440// usleep(5000000);
Dave Chapman061f3802005-11-13 20:59:30 +0000441
Dave Chapman38e8fb62005-11-08 00:52:39 +0000442 entry = tblp->addr + tblp->entryOffset;
443 if (imageno || ((int)tblp->addr & 0xffffff) != 0) {
444 memmove16(tblp->addr, tblp->addr + tblp->devOffset - padding,
445 tblp->len);
446 }
447
448 /* Return the start address in loaded image */
449 return entry;
450}
451
452/* These functions are present in the firmware library, but we reimplement
453 them here because the originals do a lot more than we want */
454
455void reset_poweroff_timer(void)
456{
457}
458
Dave Chapman38e8fb62005-11-08 00:52:39 +0000459int dbg_ports(void)
460{
461 return 0;
462}
463
464void mpeg_stop(void)
465{
466}
467
468void usb_acknowledge(void)
469{
470}
471
472void usb_wait_for_disconnect(void)
473{
474}
475
476void sys_poweroff(void)
477{
478}