blob: 5b8c0ee21706d4788401a0eeea2576e46199ad40 [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
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
Dave Chapman061f3802005-11-13 20:59:30 +0000249 snprintf(str, 80, "Model: %s, Checksum: %x", model, chksum);
Dave Chapman38e8fb62005-11-08 00:52:39 +0000250 lcd_puts(0, line++, str);
251 lcd_update();
252
253 lseek(fd, FIRMWARE_OFFSET_FILE_DATA, SEEK_SET);
254
255 rc = read(fd, buf, len);
256 if(rc < len)
Dave Chapman061f3802005-11-13 20:59:30 +0000257 return -4;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000258
259 close(fd);
260
261 sum = MODEL_NUMBER;
262
263 for(i = 0;i < len;i++) {
Dave Chapman061f3802005-11-13 20:59:30 +0000264 sum += buf[i];
Dave Chapman38e8fb62005-11-08 00:52:39 +0000265 }
266
267 snprintf(str, 80, "Sum: %x", sum);
268 lcd_puts(0, line++, str);
269 lcd_update();
270
271 if(sum != chksum)
272 return -5;
273
Dave Chapman061f3802005-11-13 20:59:30 +0000274 return len;
Dave Chapman38e8fb62005-11-08 00:52:39 +0000275}
276
Dave Chapman061f3802005-11-13 20:59:30 +0000277
278int load_linux(unsigned char* buf) {
279 int fd;
280 int rc;
281 int len;
282 char str[80];
283
284 fd=open("/linux.bin",O_RDONLY);
285 if (fd < 0)
286 return -1;
287
288 len=filesize(fd);
289 if (len > MAX_LOADSIZE)
290 return -6;
291
292 rc=read(fd,buf,len);
293
294 if (rc < len)
295 return -4;
296
297 snprintf(str, 80, "Loaded Linux: %d bytes", len);
298 lcd_puts(0, line++, str);
299 lcd_update();
300
301 return len;
302}
303
304
305/* A buffer to load the Linux kernel or Rockbox into */
306unsigned char loadbuffer[MAX_LOADSIZE];
307
Dave Chapman38e8fb62005-11-08 00:52:39 +0000308void* main(void)
309{
310 char buf[256];
311 int imageno=0;
312 int i;
313 int rc;
314 int padding = 0x4400;
315 image_t *tblp = boot_table;
316 void* entry;
317 struct partinfo* pinfo;
318 unsigned short* identify_info;
319
320 /* Turn on the backlight */
321
322#if CONFIG_BACKLIGHT==BL_IPOD4G
Dave Chapman38e8fb62005-11-08 00:52:39 +0000323 /* brightness full */
324 outl(0x80000000 | (0xff << 16), 0x7000a010);
325
Dave Chapmand8c5d1b2005-11-08 09:07:13 +0000326 /* set port B03 on */
Dave Chapman38e8fb62005-11-08 00:52:39 +0000327 outl(((0x100 | 1) << 3), 0x6000d824);
328
329#elif CONFIG_BACKLIGHT==BL_IPODNANO
330
331 /* set port B03 on */
332 outl(((0x100 | 1) << 3), 0x6000d824);
333
334 /* set port L07 on */
335 outl(((0x100 | 1) << 7), 0x6000d12c);
336
337#endif
338
339 system_init();
340 kernel_init();
341 lcd_init();
342 font_init();
343
344#if 0
345 /* ADC and button drivers are not yet implemented */
346 adc_init();
347 button_init();
348#endif
349
350 /* Notes: iPod Color/Photo LCD is 220x176, Nano is 176x138 */
351
352 /* Display the 42x47 pixel iPodLinux logo */
Dave Chapmanf5aebf72005-11-14 21:54:23 +0000353 lcd_bitmap(ipllogo, 20,6, 42,47);
Dave Chapman38e8fb62005-11-08 00:52:39 +0000354
355 /* Display the 100x31 pixel Rockbox logo */
Dave Chapmanf5aebf72005-11-14 21:54:23 +0000356 lcd_bitmap(rockboxlogo, 74,16, 100,31);
Dave Chapman38e8fb62005-11-08 00:52:39 +0000357
358 line=7;
359
360 lcd_setfont(FONT_SYSFIXED);
361
362 lcd_puts(0, line++, "iPodLinux/Rockbox boot loader");
363 snprintf(buf, sizeof(buf), "Version: 20%s", version);
364 lcd_puts(0, line++, buf);
365 snprintf(buf, sizeof(buf), "IPOD version: 0x%08x", IPOD_HW_REVISION);
366 lcd_puts(0, line++, buf);
367 lcd_update();
368
369 i=ata_init();
370 if (i==0) {
371 identify_info=ata_get_identify();
372 /* Show model */
373 for (i=0; i < 20; i++) {
374 ((unsigned short*)buf)[i]=htobe16(identify_info[i+27]);
375 }
376 buf[40]=0;
377 for (i=39; i && buf[i]==' '; i--) {
378 buf[i]=0;
379 }
380 lcd_puts(0, line++, buf);
381 lcd_update();
382 } else {
383 snprintf(buf, sizeof(buf), "ATA: %d", i);
384 lcd_puts(0, line++, buf);
385 lcd_update();
386 }
387
388 disk_init();
389 rc = disk_mount_all();
390 if (rc<=0)
391 {
392 lcd_puts(0, line++, "No partition found");
393 lcd_update();
394// while(button_get(true) != SYS_USB_CONNECTED) {};
395 }
396
397 pinfo = disk_partinfo(1);
398 snprintf(buf, sizeof(buf), "Partition 1: 0x%02x %ld MB",
399 pinfo->type, pinfo->size / 2048);
400 lcd_puts(0, line++, buf);
401 lcd_update();
402
Dave Chapman061f3802005-11-13 20:59:30 +0000403 /* Check for a keypress */
404 i=key_pressed();
Dave Chapman38e8fb62005-11-08 00:52:39 +0000405
Dave Chapman20eed882005-11-20 10:57:53 +0000406 if ((i!=BUTTON_MENU) && (i!=BUTTON_PLAY)) {
Dave Chapman061f3802005-11-13 20:59:30 +0000407 lcd_puts(0, line, "Loading Rockbox...");
408 lcd_update();
409 rc=load_rockbox(loadbuffer);
410 if (rc < 0) {
411 snprintf(buf, sizeof(buf), "Rockbox error: %d",rc);
412 lcd_puts(0, line++, buf);
413 lcd_update();
414 } else {
415 lcd_puts(0, line++, "Rockbox loaded.");
416 lcd_update();
Dave Chapman061f3802005-11-13 20:59:30 +0000417 memcpy((void*)DRAM_START,loadbuffer,rc);
418 return (void*)DRAM_START;
Dave Chapman061f3802005-11-13 20:59:30 +0000419 }
420 }
421
422 if (i==BUTTON_PLAY) {
423 lcd_puts(0, line, "Loading Linux...");
424 lcd_update();
425 rc=load_linux(loadbuffer);
426 if (rc < 0) {
427 snprintf(buf, sizeof(buf), "Linux error: %d",rc);
428 lcd_puts(0, line++, buf);
429 lcd_update();
430 } else {
431 memcpy((void*)DRAM_START,loadbuffer,rc);
432 return (void*)DRAM_START;
433 }
434 }
Dave Chapman38e8fb62005-11-08 00:52:39 +0000435
436 /* If everything else failed, try the original firmware */
Dave Chapman061f3802005-11-13 20:59:30 +0000437
Dave Chapman38e8fb62005-11-08 00:52:39 +0000438 lcd_puts(0, line, "Loading original firmware...");
439 lcd_update();
440
Dave Chapman061f3802005-11-13 20:59:30 +0000441 /* Pause for 5 seconds so we can see what's happened */
Dave Chapman20eed882005-11-20 10:57:53 +0000442// usleep(5000000);
Dave Chapman061f3802005-11-13 20:59:30 +0000443
Dave Chapman38e8fb62005-11-08 00:52:39 +0000444 entry = tblp->addr + tblp->entryOffset;
445 if (imageno || ((int)tblp->addr & 0xffffff) != 0) {
446 memmove16(tblp->addr, tblp->addr + tblp->devOffset - padding,
447 tblp->len);
448 }
449
450 /* Return the start address in loaded image */
451 return entry;
452}
453
454/* These functions are present in the firmware library, but we reimplement
455 them here because the originals do a lot more than we want */
456
457void reset_poweroff_timer(void)
458{
459}
460
Dave Chapman38e8fb62005-11-08 00:52:39 +0000461int dbg_ports(void)
462{
463 return 0;
464}
465
466void mpeg_stop(void)
467{
468}
469
470void usb_acknowledge(void)
471{
472}
473
474void usb_wait_for_disconnect(void)
475{
476}
477
478void sys_poweroff(void)
479{
480}