blob: 4de6b61e62d523ee780f125bc286546ee0305f54 [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;
343
Dave Chapman38e8fb62005-11-08 00:52:39 +0000344 system_init();
345 kernel_init();
346 lcd_init();
347 font_init();
348
349#if 0
350 /* ADC and button drivers are not yet implemented */
351 adc_init();
352 button_init();
353#endif
354
Dave Chapman3ded3ce2006-01-18 19:52:02 +0000355 line=0;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000356
357 lcd_setfont(FONT_SYSFIXED);
358
Dave Chapman3ded3ce2006-01-18 19:52:02 +0000359 lcd_puts(0, line++, "Rockbox boot loader");
Dave Chapman38e8fb62005-11-08 00:52:39 +0000360 snprintf(buf, sizeof(buf), "Version: 20%s", version);
361 lcd_puts(0, line++, buf);
362 snprintf(buf, sizeof(buf), "IPOD version: 0x%08x", IPOD_HW_REVISION);
363 lcd_puts(0, line++, buf);
364 lcd_update();
365
366 i=ata_init();
367 if (i==0) {
368 identify_info=ata_get_identify();
369 /* Show model */
370 for (i=0; i < 20; i++) {
371 ((unsigned short*)buf)[i]=htobe16(identify_info[i+27]);
372 }
373 buf[40]=0;
374 for (i=39; i && buf[i]==' '; i--) {
375 buf[i]=0;
376 }
377 lcd_puts(0, line++, buf);
378 lcd_update();
379 } else {
380 snprintf(buf, sizeof(buf), "ATA: %d", i);
381 lcd_puts(0, line++, buf);
382 lcd_update();
383 }
384
385 disk_init();
386 rc = disk_mount_all();
387 if (rc<=0)
388 {
389 lcd_puts(0, line++, "No partition found");
390 lcd_update();
391// while(button_get(true) != SYS_USB_CONNECTED) {};
392 }
393
394 pinfo = disk_partinfo(1);
395 snprintf(buf, sizeof(buf), "Partition 1: 0x%02x %ld MB",
396 pinfo->type, pinfo->size / 2048);
397 lcd_puts(0, line++, buf);
398 lcd_update();
399
Dave Chapman061f3802005-11-13 20:59:30 +0000400 /* Check for a keypress */
401 i=key_pressed();
Dave Chapman38e8fb62005-11-08 00:52:39 +0000402
Dave Chapman20eed882005-11-20 10:57:53 +0000403 if ((i!=BUTTON_MENU) && (i!=BUTTON_PLAY)) {
Dave Chapman061f3802005-11-13 20:59:30 +0000404 lcd_puts(0, line, "Loading Rockbox...");
405 lcd_update();
406 rc=load_rockbox(loadbuffer);
407 if (rc < 0) {
408 snprintf(buf, sizeof(buf), "Rockbox error: %d",rc);
409 lcd_puts(0, line++, buf);
410 lcd_update();
411 } else {
412 lcd_puts(0, line++, "Rockbox loaded.");
413 lcd_update();
Dave Chapman061f3802005-11-13 20:59:30 +0000414 memcpy((void*)DRAM_START,loadbuffer,rc);
Dave Chapmancb7e6952006-01-05 17:02:48 +0000415
416 /* Transfer execution directly to Rockbox - we don't want
417 to run the rest of the bootloader startup code. */
418 asm volatile(
419 "mov r0, #0x10000000 \n"
420 "mov pc, r0 \n"
421 );
422
423 /* We don't get here, but keep the compiler happy. */
424 return (void*)0;
Dave Chapman061f3802005-11-13 20:59:30 +0000425 }
426 }
427
428 if (i==BUTTON_PLAY) {
429 lcd_puts(0, line, "Loading Linux...");
430 lcd_update();
431 rc=load_linux(loadbuffer);
432 if (rc < 0) {
433 snprintf(buf, sizeof(buf), "Linux error: %d",rc);
434 lcd_puts(0, line++, buf);
435 lcd_update();
436 } else {
437 memcpy((void*)DRAM_START,loadbuffer,rc);
438 return (void*)DRAM_START;
439 }
440 }
Dave Chapman38e8fb62005-11-08 00:52:39 +0000441
442 /* If everything else failed, try the original firmware */
Dave Chapman061f3802005-11-13 20:59:30 +0000443
Dave Chapman38e8fb62005-11-08 00:52:39 +0000444 lcd_puts(0, line, "Loading original firmware...");
445 lcd_update();
446
Dave Chapman061f3802005-11-13 20:59:30 +0000447 /* Pause for 5 seconds so we can see what's happened */
Dave Chapman20eed882005-11-20 10:57:53 +0000448// usleep(5000000);
Dave Chapman061f3802005-11-13 20:59:30 +0000449
Dave Chapman38e8fb62005-11-08 00:52:39 +0000450 entry = tblp->addr + tblp->entryOffset;
451 if (imageno || ((int)tblp->addr & 0xffffff) != 0) {
452 memmove16(tblp->addr, tblp->addr + tblp->devOffset - padding,
453 tblp->len);
454 }
455
456 /* Return the start address in loaded image */
457 return entry;
458}
459
460/* These functions are present in the firmware library, but we reimplement
461 them here because the originals do a lot more than we want */
462
463void reset_poweroff_timer(void)
464{
465}
466
Dave Chapman38e8fb62005-11-08 00:52:39 +0000467int dbg_ports(void)
468{
469 return 0;
470}
471
472void mpeg_stop(void)
473{
474}
475
476void usb_acknowledge(void)
477{
478}
479
480void usb_wait_for_disconnect(void)
481{
482}
483
484void sys_poweroff(void)
485{
486}