blob: 591b7608aae43428b4d6fbf81b92aba754c444e0 [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
57#include "rockbox-16bit.h"
58#include "ipodlinux-16bit.h"
59
60typedef struct _image {
61 unsigned type; /* '' */
62 unsigned id; /* */
63 unsigned pad1; /* 0000 0000 */
64 unsigned devOffset; /* byte offset of start of image code */
65 unsigned len; /* length in bytes of image */
66 void *addr; /* load address */
67 unsigned entryOffset; /* execution start within image */
68 unsigned chksum; /* checksum for image */
69 unsigned vers; /* image version */
70 unsigned loadAddr; /* load address for image */
71} image_t;
72
73extern image_t boot_table[];
74
75int line=0;
76
77static void memmove16(void *dest, const void *src, unsigned count)
78{
79 struct bufstr {
80 unsigned _buf[4];
81 } *d, *s;
82
83 if (src >= dest) {
84 count = (count + 15) >> 4;
85 d = (struct bufstr *) dest;
86 s = (struct bufstr *) src;
87 while (count--)
88 *d++ = *s++;
89 } else {
90 count = (count + 15) >> 4;
91 d = (struct bufstr *)(dest + (count <<4));
92 s = (struct bufstr *)(src + (count <<4));
93 while (count--)
94 *--d = *--s;
95 }
96}
97
98/* get current usec counter */
99int timer_get_current(void)
100{
101 return inl(IPOD_PP5020_RTC);
102}
103
104/* check if number of seconds has past */
105int timer_check(int clock_start, unsigned int usecs)
106{
107 if ((inl(IPOD_PP5020_RTC) - clock_start) >= usecs) {
108 return 1;
109 } else {
110 return 0;
111 }
112}
113
114/* This isn't a sleep, but let's call it that. */
115int usleep(unsigned int usecs)
116{
117 unsigned int start = inl(IPOD_PP5020_RTC);
118
119 while ((inl(IPOD_PP5020_RTC) - start) < usecs) {
120 // empty
121 }
122
123 return 0;
124}
125
Dave Chapman061f3802005-11-13 20:59:30 +0000126
127static void ser_opto_keypad_cfg(int val)
128{
129 int start_time;
130
131 outl(inl(0x6000d004) & ~0x80, 0x6000d004);
132
133 outl(inl(0x7000c104) | 0xc000000, 0x7000c104);
134 outl(val, 0x7000c120);
135 outl(inl(0x7000c100) | 0x80000000, 0x7000c100);
136
137 outl(inl(0x6000d024) & ~0x10, 0x6000d024);
138 outl(inl(0x6000d014) | 0x10, 0x6000d014);
139
140 start_time = timer_get_current();
141 do {
142 if ((inl(0x7000c104) & 0x80000000) == 0) {
143 break;
144 }
145 } while (timer_check(start_time, 1500) != 0);
146
147 outl(inl(0x7000c100) & ~0x80000000, 0x7000c100);
148
149 outl(inl(0x6000d004) | 0x80, 0x6000d004);
150 outl(inl(0x6000d024) | 0x10, 0x6000d024);
151 outl(inl(0x6000d014) & ~0x10, 0x6000d014);
152
153 outl(inl(0x7000c104) | 0xc000000, 0x7000c104);
154 outl(inl(0x7000c100) | 0x60000000, 0x7000c100);
155}
156
157int opto_keypad_read(void)
158{
159 int loop_cnt, had_io = 0;
160
161 for (loop_cnt = 5; loop_cnt != 0;)
162 {
163 int key_pressed = 0;
164 int start_time;
165 unsigned int key_pad_val;
166
167 ser_opto_keypad_cfg(0x8000023a);
168
169 start_time = timer_get_current();
170 do {
171 if (inl(0x7000c104) & 0x4000000) {
172 had_io = 1;
173 break;
174 }
175
176 if (had_io != 0) {
177 break;
178 }
179 } while (timer_check(start_time, 1500) != 0);
180
181 key_pad_val = inl(0x7000c140);
182 if ((key_pad_val & ~0x7fff0000) != 0x8000023a) {
183 loop_cnt--;
184 } else {
185 key_pad_val = (key_pad_val << 11) >> 27;
186 key_pressed = 1;
187 }
188
189 outl(inl(0x7000c100) | 0x60000000, 0x7000c100);
190 outl(inl(0x7000c104) | 0xc000000, 0x7000c104);
191
192 if (key_pressed != 0) {
193 return key_pad_val ^ 0x1f;
194 }
195 }
196
197 return 0;
198}
199
200static int key_pressed(void)
201{
202 unsigned char state;
203
204 state = opto_keypad_read();
205 if ((state & 0x4) == 0) return BUTTON_LEFT;
206 if ((state & 0x10) == 0) return BUTTON_MENU;
207 if ((state & 0x8) == 0) return BUTTON_PLAY;
208 if ((state & 0x2) == 0) return BUTTON_RIGHT;
209 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
223 fd = open("/rockbox.ipod", O_RDONLY);
224 if(fd < 0)
Dave Chapman061f3802005-11-13 20:59:30 +0000225 return -1;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000226
227 len = filesize(fd) - 8;
228
Dave Chapman061f3802005-11-13 20:59:30 +0000229 if (len > MAX_LOADSIZE)
230 return -6;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000231
232 lseek(fd, FIRMWARE_OFFSET_FILE_CRC, SEEK_SET);
233
234 rc = read(fd, &chksum, 4);
Dave Chapman061f3802005-11-13 20:59:30 +0000235 chksum=betoh32(chksum); /* Rockbox checksums are big-endian */
Dave Chapman38e8fb62005-11-08 00:52:39 +0000236 if(rc < 4)
Dave Chapman061f3802005-11-13 20:59:30 +0000237 return -2;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000238
239 rc = read(fd, model, 4);
240 if(rc < 4)
Dave Chapman061f3802005-11-13 20:59:30 +0000241 return -3;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000242
243 model[4] = 0;
244
Dave Chapman061f3802005-11-13 20:59:30 +0000245 snprintf(str, 80, "Model: %s, Checksum: %x", model, chksum);
Dave Chapman38e8fb62005-11-08 00:52:39 +0000246 lcd_puts(0, line++, str);
247 lcd_update();
248
249 lseek(fd, FIRMWARE_OFFSET_FILE_DATA, SEEK_SET);
250
251 rc = read(fd, buf, len);
252 if(rc < len)
Dave Chapman061f3802005-11-13 20:59:30 +0000253 return -4;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000254
255 close(fd);
256
257 sum = MODEL_NUMBER;
258
259 for(i = 0;i < len;i++) {
Dave Chapman061f3802005-11-13 20:59:30 +0000260 sum += buf[i];
Dave Chapman38e8fb62005-11-08 00:52:39 +0000261 }
262
263 snprintf(str, 80, "Sum: %x", sum);
264 lcd_puts(0, line++, str);
265 lcd_update();
266
267 if(sum != chksum)
268 return -5;
269
Dave Chapman061f3802005-11-13 20:59:30 +0000270 return len;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000271}
272
Dave Chapman061f3802005-11-13 20:59:30 +0000273
274int load_linux(unsigned char* buf) {
275 int fd;
276 int rc;
277 int len;
278 char str[80];
279
280 fd=open("/linux.bin",O_RDONLY);
281 if (fd < 0)
282 return -1;
283
284 len=filesize(fd);
285 if (len > MAX_LOADSIZE)
286 return -6;
287
288 rc=read(fd,buf,len);
289
290 if (rc < len)
291 return -4;
292
293 snprintf(str, 80, "Loaded Linux: %d bytes", len);
294 lcd_puts(0, line++, str);
295 lcd_update();
296
297 return len;
298}
299
300
301/* A buffer to load the Linux kernel or Rockbox into */
302unsigned char loadbuffer[MAX_LOADSIZE];
303
Dave Chapman38e8fb62005-11-08 00:52:39 +0000304void* main(void)
305{
306 char buf[256];
307 int imageno=0;
308 int i;
309 int rc;
310 int padding = 0x4400;
311 image_t *tblp = boot_table;
312 void* entry;
313 struct partinfo* pinfo;
314 unsigned short* identify_info;
315
316 /* Turn on the backlight */
317
318#if CONFIG_BACKLIGHT==BL_IPOD4G
Dave Chapman38e8fb62005-11-08 00:52:39 +0000319 /* brightness full */
320 outl(0x80000000 | (0xff << 16), 0x7000a010);
321
Dave Chapmand8c5d1b2005-11-08 09:07:13 +0000322 /* set port B03 on */
Dave Chapman38e8fb62005-11-08 00:52:39 +0000323 outl(((0x100 | 1) << 3), 0x6000d824);
324
325#elif CONFIG_BACKLIGHT==BL_IPODNANO
326
327 /* set port B03 on */
328 outl(((0x100 | 1) << 3), 0x6000d824);
329
330 /* set port L07 on */
331 outl(((0x100 | 1) << 7), 0x6000d12c);
332
333#endif
334
335 system_init();
336 kernel_init();
337 lcd_init();
338 font_init();
339
340#if 0
341 /* ADC and button drivers are not yet implemented */
342 adc_init();
343 button_init();
344#endif
345
346 /* Notes: iPod Color/Photo LCD is 220x176, Nano is 176x138 */
347
348 /* Display the 42x47 pixel iPodLinux logo */
349 lcd_bitmap((unsigned char*)ipllogo, 20,6, 42,47);
350
351 /* Display the 100x31 pixel Rockbox logo */
352 lcd_bitmap((unsigned char*)rockboxlogo, 74,16, 100,31);
353
354 line=7;
355
356 lcd_setfont(FONT_SYSFIXED);
357
358 lcd_puts(0, line++, "iPodLinux/Rockbox boot loader");
359 snprintf(buf, sizeof(buf), "Version: 20%s", version);
360 lcd_puts(0, line++, buf);
361 snprintf(buf, sizeof(buf), "IPOD version: 0x%08x", IPOD_HW_REVISION);
362 lcd_puts(0, line++, buf);
363 lcd_update();
364
365 i=ata_init();
366 if (i==0) {
367 identify_info=ata_get_identify();
368 /* Show model */
369 for (i=0; i < 20; i++) {
370 ((unsigned short*)buf)[i]=htobe16(identify_info[i+27]);
371 }
372 buf[40]=0;
373 for (i=39; i && buf[i]==' '; i--) {
374 buf[i]=0;
375 }
376 lcd_puts(0, line++, buf);
377 lcd_update();
378 } else {
379 snprintf(buf, sizeof(buf), "ATA: %d", i);
380 lcd_puts(0, line++, buf);
381 lcd_update();
382 }
383
384 disk_init();
385 rc = disk_mount_all();
386 if (rc<=0)
387 {
388 lcd_puts(0, line++, "No partition found");
389 lcd_update();
390// while(button_get(true) != SYS_USB_CONNECTED) {};
391 }
392
393 pinfo = disk_partinfo(1);
394 snprintf(buf, sizeof(buf), "Partition 1: 0x%02x %ld MB",
395 pinfo->type, pinfo->size / 2048);
396 lcd_puts(0, line++, buf);
397 lcd_update();
398
Dave Chapman061f3802005-11-13 20:59:30 +0000399 /* Check for a keypress */
400 i=key_pressed();
Dave Chapman38e8fb62005-11-08 00:52:39 +0000401
Dave Chapman061f3802005-11-13 20:59:30 +0000402 if (i==BUTTON_MENU) {
403 lcd_puts(0, line, "Loading Rockbox...");
404 lcd_update();
405 rc=load_rockbox(loadbuffer);
406 if (rc < 0) {
407 snprintf(buf, sizeof(buf), "Rockbox error: %d",rc);
408 lcd_puts(0, line++, buf);
409 lcd_update();
410 } else {
411 lcd_puts(0, line++, "Rockbox loaded.");
412 lcd_update();
413#if 0
414 /* Rockbox is not yet runable, so we disable this */
415 memcpy((void*)DRAM_START,loadbuffer,rc);
416 return (void*)DRAM_START;
417#endif
418 }
419 }
420
421 if (i==BUTTON_PLAY) {
422 lcd_puts(0, line, "Loading Linux...");
423 lcd_update();
424 rc=load_linux(loadbuffer);
425 if (rc < 0) {
426 snprintf(buf, sizeof(buf), "Linux error: %d",rc);
427 lcd_puts(0, line++, buf);
428 lcd_update();
429 } else {
430 memcpy((void*)DRAM_START,loadbuffer,rc);
431 return (void*)DRAM_START;
432 }
433 }
Dave Chapman38e8fb62005-11-08 00:52:39 +0000434
435 /* If everything else failed, try the original firmware */
Dave Chapman061f3802005-11-13 20:59:30 +0000436
Dave Chapman38e8fb62005-11-08 00:52:39 +0000437 lcd_puts(0, line, "Loading original firmware...");
438 lcd_update();
439
Dave Chapman061f3802005-11-13 20:59:30 +0000440 /* Pause for 5 seconds so we can see what's happened */
441 usleep(5000000);
442
Dave Chapman38e8fb62005-11-08 00:52:39 +0000443 entry = tblp->addr + tblp->entryOffset;
444 if (imageno || ((int)tblp->addr & 0xffffff) != 0) {
445 memmove16(tblp->addr, tblp->addr + tblp->devOffset - padding,
446 tblp->len);
447 }
448
449 /* Return the start address in loaded image */
450 return entry;
451}
452
453/* These functions are present in the firmware library, but we reimplement
454 them here because the originals do a lot more than we want */
455
456void reset_poweroff_timer(void)
457{
458}
459
Dave Chapman38e8fb62005-11-08 00:52:39 +0000460int dbg_ports(void)
461{
462 return 0;
463}
464
465void mpeg_stop(void)
466{
467}
468
469void usb_acknowledge(void)
470{
471}
472
473void usb_wait_for_disconnect(void)
474{
475}
476
477void sys_poweroff(void)
478{
479}