blob: 97a4cebd69fa02aaa242845c1010aeb6c2f05167 [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 Chapmanf9dac402006-01-31 01:50:07 +000047/* We copy the hardware revision to the last four bytes of SDRAM and then
48 re-read it after we have re-mapped SDRAM to 0x0 in Rockbox */
49#define TMP_IPOD_HW_REVISION (*((volatile unsigned long*)(0x11fffffc)))
50
Dave Chapman061f3802005-11-13 20:59:30 +000051#define BUTTON_LEFT 1
52#define BUTTON_MENU 2
53#define BUTTON_RIGHT 3
54#define BUTTON_PLAY 4
55
56/* Size of the buffer to store the loaded Rockbox/Linux image */
57#define MAX_LOADSIZE (4*1024*1024)
58
Dave Chapman38e8fb62005-11-08 00:52:39 +000059char version[] = APPSVERSION;
60
Dave Chapman38e8fb62005-11-08 00:52:39 +000061typedef struct _image {
62 unsigned type; /* '' */
63 unsigned id; /* */
64 unsigned pad1; /* 0000 0000 */
65 unsigned devOffset; /* byte offset of start of image code */
66 unsigned len; /* length in bytes of image */
67 void *addr; /* load address */
68 unsigned entryOffset; /* execution start within image */
69 unsigned chksum; /* checksum for image */
70 unsigned vers; /* image version */
71 unsigned loadAddr; /* load address for image */
72} image_t;
73
74extern image_t boot_table[];
75
76int line=0;
77
78static void memmove16(void *dest, const void *src, unsigned count)
79{
80 struct bufstr {
81 unsigned _buf[4];
82 } *d, *s;
83
84 if (src >= dest) {
85 count = (count + 15) >> 4;
86 d = (struct bufstr *) dest;
87 s = (struct bufstr *) src;
88 while (count--)
89 *d++ = *s++;
90 } else {
91 count = (count + 15) >> 4;
92 d = (struct bufstr *)(dest + (count <<4));
93 s = (struct bufstr *)(src + (count <<4));
94 while (count--)
95 *--d = *--s;
96 }
97}
98
99/* get current usec counter */
100int timer_get_current(void)
101{
102 return inl(IPOD_PP5020_RTC);
103}
104
105/* check if number of seconds has past */
106int timer_check(int clock_start, unsigned int usecs)
107{
108 if ((inl(IPOD_PP5020_RTC) - clock_start) >= usecs) {
109 return 1;
110 } else {
111 return 0;
112 }
113}
114
115/* This isn't a sleep, but let's call it that. */
116int usleep(unsigned int usecs)
117{
118 unsigned int start = inl(IPOD_PP5020_RTC);
119
120 while ((inl(IPOD_PP5020_RTC) - start) < usecs) {
121 // empty
122 }
123
124 return 0;
125}
126
Dave Chapman061f3802005-11-13 20:59:30 +0000127
128static void ser_opto_keypad_cfg(int val)
129{
130 int start_time;
131
132 outl(inl(0x6000d004) & ~0x80, 0x6000d004);
133
134 outl(inl(0x7000c104) | 0xc000000, 0x7000c104);
135 outl(val, 0x7000c120);
136 outl(inl(0x7000c100) | 0x80000000, 0x7000c100);
137
138 outl(inl(0x6000d024) & ~0x10, 0x6000d024);
139 outl(inl(0x6000d014) | 0x10, 0x6000d014);
140
141 start_time = timer_get_current();
142 do {
143 if ((inl(0x7000c104) & 0x80000000) == 0) {
144 break;
145 }
146 } while (timer_check(start_time, 1500) != 0);
147
148 outl(inl(0x7000c100) & ~0x80000000, 0x7000c100);
149
150 outl(inl(0x6000d004) | 0x80, 0x6000d004);
151 outl(inl(0x6000d024) | 0x10, 0x6000d024);
152 outl(inl(0x6000d014) & ~0x10, 0x6000d014);
153
154 outl(inl(0x7000c104) | 0xc000000, 0x7000c104);
155 outl(inl(0x7000c100) | 0x60000000, 0x7000c100);
156}
157
158int opto_keypad_read(void)
159{
160 int loop_cnt, had_io = 0;
161
162 for (loop_cnt = 5; loop_cnt != 0;)
163 {
164 int key_pressed = 0;
165 int start_time;
166 unsigned int key_pad_val;
167
168 ser_opto_keypad_cfg(0x8000023a);
169
170 start_time = timer_get_current();
171 do {
172 if (inl(0x7000c104) & 0x4000000) {
173 had_io = 1;
174 break;
175 }
176
177 if (had_io != 0) {
178 break;
179 }
180 } while (timer_check(start_time, 1500) != 0);
181
182 key_pad_val = inl(0x7000c140);
183 if ((key_pad_val & ~0x7fff0000) != 0x8000023a) {
184 loop_cnt--;
185 } else {
186 key_pad_val = (key_pad_val << 11) >> 27;
187 key_pressed = 1;
188 }
189
190 outl(inl(0x7000c100) | 0x60000000, 0x7000c100);
191 outl(inl(0x7000c104) | 0xc000000, 0x7000c104);
192
193 if (key_pressed != 0) {
194 return key_pad_val ^ 0x1f;
195 }
196 }
197
198 return 0;
199}
200
201static int key_pressed(void)
202{
203 unsigned char state;
204
205 state = opto_keypad_read();
206 if ((state & 0x4) == 0) return BUTTON_LEFT;
207 if ((state & 0x10) == 0) return BUTTON_MENU;
208 if ((state & 0x8) == 0) return BUTTON_PLAY;
209 if ((state & 0x2) == 0) return BUTTON_RIGHT;
210 return 0;
211}
212
213int load_rockbox(unsigned char* buf)
Dave Chapman38e8fb62005-11-08 00:52:39 +0000214{
215 int fd;
216 int rc;
217 int len;
218 unsigned long chksum;
219 char model[5];
220 unsigned long sum;
221 int i;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000222 char str[80];
223
Dave Chapman20eed882005-11-20 10:57:53 +0000224 fd = open("/.rockbox/" BOOTFILE, O_RDONLY);
Dave Chapman38e8fb62005-11-08 00:52:39 +0000225 if(fd < 0)
Dave Chapman20eed882005-11-20 10:57:53 +0000226 {
227 fd = open("/" BOOTFILE, O_RDONLY);
228 if(fd < 0)
229 return -1;
230 }
Dave Chapman38e8fb62005-11-08 00:52:39 +0000231
232 len = filesize(fd) - 8;
233
Dave Chapman061f3802005-11-13 20:59:30 +0000234 if (len > MAX_LOADSIZE)
235 return -6;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000236
237 lseek(fd, FIRMWARE_OFFSET_FILE_CRC, SEEK_SET);
238
239 rc = read(fd, &chksum, 4);
Dave Chapman061f3802005-11-13 20:59:30 +0000240 chksum=betoh32(chksum); /* Rockbox checksums are big-endian */
Dave Chapman38e8fb62005-11-08 00:52:39 +0000241 if(rc < 4)
Dave Chapman061f3802005-11-13 20:59:30 +0000242 return -2;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000243
244 rc = read(fd, model, 4);
245 if(rc < 4)
Dave Chapman061f3802005-11-13 20:59:30 +0000246 return -3;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000247
248 model[4] = 0;
249
Thom Johansen1c69caf2006-01-18 23:02:00 +0000250 snprintf(str, 80, "Model: %s", model);
251 lcd_puts(0, line++, str);
252 snprintf(str, 80, "Checksum: %x", chksum);
Dave Chapman38e8fb62005-11-08 00:52:39 +0000253 lcd_puts(0, line++, str);
254 lcd_update();
255
256 lseek(fd, FIRMWARE_OFFSET_FILE_DATA, SEEK_SET);
257
258 rc = read(fd, buf, len);
259 if(rc < len)
Dave Chapman061f3802005-11-13 20:59:30 +0000260 return -4;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000261
262 close(fd);
263
264 sum = MODEL_NUMBER;
265
266 for(i = 0;i < len;i++) {
Dave Chapman061f3802005-11-13 20:59:30 +0000267 sum += buf[i];
Dave Chapman38e8fb62005-11-08 00:52:39 +0000268 }
269
270 snprintf(str, 80, "Sum: %x", sum);
271 lcd_puts(0, line++, str);
272 lcd_update();
273
274 if(sum != chksum)
275 return -5;
276
Dave Chapman061f3802005-11-13 20:59:30 +0000277 return len;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000278}
279
Dave Chapman061f3802005-11-13 20:59:30 +0000280
281int load_linux(unsigned char* buf) {
282 int fd;
283 int rc;
284 int len;
285 char str[80];
286
287 fd=open("/linux.bin",O_RDONLY);
288 if (fd < 0)
289 return -1;
290
291 len=filesize(fd);
292 if (len > MAX_LOADSIZE)
293 return -6;
294
295 rc=read(fd,buf,len);
296
297 if (rc < len)
298 return -4;
299
300 snprintf(str, 80, "Loaded Linux: %d bytes", len);
301 lcd_puts(0, line++, str);
302 lcd_update();
303
304 return len;
305}
306
307
308/* A buffer to load the Linux kernel or Rockbox into */
309unsigned char loadbuffer[MAX_LOADSIZE];
310
Dave Chapman38e8fb62005-11-08 00:52:39 +0000311void* main(void)
312{
313 char buf[256];
314 int imageno=0;
315 int i;
316 int rc;
317 int padding = 0x4400;
318 image_t *tblp = boot_table;
319 void* entry;
320 struct partinfo* pinfo;
321 unsigned short* identify_info;
322
323 /* Turn on the backlight */
324
325#if CONFIG_BACKLIGHT==BL_IPOD4G
Dave Chapman38e8fb62005-11-08 00:52:39 +0000326 /* brightness full */
327 outl(0x80000000 | (0xff << 16), 0x7000a010);
328
Dave Chapmand8c5d1b2005-11-08 09:07:13 +0000329 /* set port B03 on */
Dave Chapman38e8fb62005-11-08 00:52:39 +0000330 outl(((0x100 | 1) << 3), 0x6000d824);
331
332#elif CONFIG_BACKLIGHT==BL_IPODNANO
333
334 /* set port B03 on */
335 outl(((0x100 | 1) << 3), 0x6000d824);
336
337 /* set port L07 on */
338 outl(((0x100 | 1) << 7), 0x6000d12c);
339
340#endif
341
Dave Chapmanf9dac402006-01-31 01:50:07 +0000342 TMP_IPOD_HW_REVISION = IPOD_HW_REVISION;
Dave Chapman1446b212006-01-31 09:40:21 +0000343 ipod_hw_rev = IPOD_HW_REVISION;
Dave Chapmanf9dac402006-01-31 01:50:07 +0000344
Dave Chapman38e8fb62005-11-08 00:52:39 +0000345 system_init();
346 kernel_init();
347 lcd_init();
348 font_init();
349
350#if 0
351 /* ADC and button drivers are not yet implemented */
352 adc_init();
353 button_init();
354#endif
355
Dave Chapman3ded3ce2006-01-18 19:52:02 +0000356 line=0;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000357
358 lcd_setfont(FONT_SYSFIXED);
359
Dave Chapman3ded3ce2006-01-18 19:52:02 +0000360 lcd_puts(0, line++, "Rockbox boot loader");
Dave Chapman38e8fb62005-11-08 00:52:39 +0000361 snprintf(buf, sizeof(buf), "Version: 20%s", version);
362 lcd_puts(0, line++, buf);
363 snprintf(buf, sizeof(buf), "IPOD version: 0x%08x", IPOD_HW_REVISION);
364 lcd_puts(0, line++, buf);
365 lcd_update();
366
367 i=ata_init();
368 if (i==0) {
369 identify_info=ata_get_identify();
370 /* Show model */
371 for (i=0; i < 20; i++) {
372 ((unsigned short*)buf)[i]=htobe16(identify_info[i+27]);
373 }
374 buf[40]=0;
375 for (i=39; i && buf[i]==' '; i--) {
376 buf[i]=0;
377 }
378 lcd_puts(0, line++, buf);
379 lcd_update();
380 } else {
381 snprintf(buf, sizeof(buf), "ATA: %d", i);
382 lcd_puts(0, line++, buf);
383 lcd_update();
384 }
385
386 disk_init();
387 rc = disk_mount_all();
388 if (rc<=0)
389 {
390 lcd_puts(0, line++, "No partition found");
391 lcd_update();
392// while(button_get(true) != SYS_USB_CONNECTED) {};
393 }
394
395 pinfo = disk_partinfo(1);
396 snprintf(buf, sizeof(buf), "Partition 1: 0x%02x %ld MB",
397 pinfo->type, pinfo->size / 2048);
398 lcd_puts(0, line++, buf);
399 lcd_update();
400
Dave Chapman061f3802005-11-13 20:59:30 +0000401 /* Check for a keypress */
402 i=key_pressed();
Dave Chapman38e8fb62005-11-08 00:52:39 +0000403
Dave Chapman20eed882005-11-20 10:57:53 +0000404 if ((i!=BUTTON_MENU) && (i!=BUTTON_PLAY)) {
Dave Chapman061f3802005-11-13 20:59:30 +0000405 lcd_puts(0, line, "Loading Rockbox...");
406 lcd_update();
407 rc=load_rockbox(loadbuffer);
408 if (rc < 0) {
409 snprintf(buf, sizeof(buf), "Rockbox error: %d",rc);
410 lcd_puts(0, line++, buf);
411 lcd_update();
412 } else {
413 lcd_puts(0, line++, "Rockbox loaded.");
414 lcd_update();
Dave Chapman061f3802005-11-13 20:59:30 +0000415 memcpy((void*)DRAM_START,loadbuffer,rc);
Dave Chapmancb7e6952006-01-05 17:02:48 +0000416
417 /* Transfer execution directly to Rockbox - we don't want
418 to run the rest of the bootloader startup code. */
419 asm volatile(
420 "mov r0, #0x10000000 \n"
421 "mov pc, r0 \n"
422 );
423
424 /* We don't get here, but keep the compiler happy. */
425 return (void*)0;
Dave Chapman061f3802005-11-13 20:59:30 +0000426 }
427 }
428
429 if (i==BUTTON_PLAY) {
430 lcd_puts(0, line, "Loading Linux...");
431 lcd_update();
432 rc=load_linux(loadbuffer);
433 if (rc < 0) {
434 snprintf(buf, sizeof(buf), "Linux error: %d",rc);
435 lcd_puts(0, line++, buf);
436 lcd_update();
437 } else {
438 memcpy((void*)DRAM_START,loadbuffer,rc);
439 return (void*)DRAM_START;
440 }
441 }
Dave Chapman38e8fb62005-11-08 00:52:39 +0000442
443 /* If everything else failed, try the original firmware */
Dave Chapman061f3802005-11-13 20:59:30 +0000444
Dave Chapman38e8fb62005-11-08 00:52:39 +0000445 lcd_puts(0, line, "Loading original firmware...");
446 lcd_update();
447
Dave Chapman061f3802005-11-13 20:59:30 +0000448 /* Pause for 5 seconds so we can see what's happened */
Dave Chapman20eed882005-11-20 10:57:53 +0000449// usleep(5000000);
Dave Chapman061f3802005-11-13 20:59:30 +0000450
Dave Chapman38e8fb62005-11-08 00:52:39 +0000451 entry = tblp->addr + tblp->entryOffset;
452 if (imageno || ((int)tblp->addr & 0xffffff) != 0) {
453 memmove16(tblp->addr, tblp->addr + tblp->devOffset - padding,
454 tblp->len);
455 }
456
457 /* Return the start address in loaded image */
458 return entry;
459}
460
461/* These functions are present in the firmware library, but we reimplement
462 them here because the originals do a lot more than we want */
463
464void reset_poweroff_timer(void)
465{
466}
467
Dave Chapman38e8fb62005-11-08 00:52:39 +0000468int dbg_ports(void)
469{
470 return 0;
471}
472
473void mpeg_stop(void)
474{
475}
476
477void usb_acknowledge(void)
478{
479}
480
481void usb_wait_for_disconnect(void)
482{
483}
484
485void sys_poweroff(void)
486{
487}