1/* 2 * arch/ppc/boot/spruce/misc.c 3 * 4 * Misc. bootloader code for IBM Spruce reference platform 5 * 6 * Authors: Johnnie Peters <jpeters@mvista.com> 7 * Matt Porter <mporter@mvista.com> 8 * 9 * Derived from arch/ppc/boot/prep/misc.c 10 * 11 * Copyright 2000-2001 MontaVista Software Inc. 12 * 13 * This program is free software; you can redistribute it and/or modify it 14 * under the terms of the GNU General Public License as published by the 15 * Free Software Foundation; either version 2 of the License, or (at your 16 * option) any later version. 17 * 18 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 19 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 20 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 21 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 24 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 * 29 * You should have received a copy of the GNU General Public License along 30 * with this program; if not, write to the Free Software Foundation, Inc., 31 * 675 Mass Ave, Cambridge, MA 02139, USA. 32 */ 33 34#include <linux/types.h> 35#include <linux/elf.h> 36#include <linux/config.h> 37#include <linux/pci.h> 38 39#include <asm/page.h> 40#include <asm/processor.h> 41#include <asm/mmu.h> 42#include <asm/bootinfo.h> 43 44#include "zlib.h" 45 46/* Define some important locations of the Spruce. */ 47#define SPRUCE_PCI_CONFIG_ADDR 0xfec00000 48#define SPRUCE_PCI_CONFIG_DATA 0xfec00004 49#define SPRUCE_ISA_IO_BASE 0xf8000000 50 51unsigned long com_port; 52 53char *avail_ram; 54char *end_avail; 55 56/* The linker tells us where the image is. */ 57extern char __image_begin, __image_end; 58extern char __ramdisk_begin, __ramdisk_end; 59extern char _end[]; 60 61#ifdef CONFIG_CMDLINE 62#define CMDLINE CONFIG_CMDLINE 63#else 64#define CMDLINE "" 65#endif 66char cmd_preset[] = CMDLINE; 67char cmd_buf[256]; 68char *cmd_line = cmd_buf; 69 70unsigned long initrd_size = 0; 71 72char *zimage_start; 73int zimage_size; 74 75extern void udelay(long); 76extern void puts(const char *); 77extern void putc(const char c); 78extern void puthex(unsigned long val); 79extern int getc(void); 80extern int tstc(void); 81extern void gunzip(void *, int, unsigned char *, int *); 82 83extern unsigned long serial_init(int chan, void *ignored); 84 85/* PCI configuration space access routines. */ 86unsigned int *pci_config_address = (unsigned int *)SPRUCE_PCI_CONFIG_ADDR; 87unsigned char *pci_config_data = (unsigned char *)SPRUCE_PCI_CONFIG_DATA; 88 89void cpc700_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, 90 unsigned char offset, unsigned char *val) 91{ 92 out_le32(pci_config_address, 93 (((bus & 0xff)<<16) | (dev_fn<<8) | (offset&0xfc) | 0x80000000)); 94 95 *val= (in_le32((unsigned *)pci_config_data) >> (8 * (offset & 3))) & 0xff; 96} 97 98void cpc700_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, 99 unsigned char offset, unsigned char val) 100{ 101 out_le32(pci_config_address, 102 (((bus & 0xff)<<16) | (dev_fn<<8) | (offset&0xfc) | 0x80000000)); 103 104 out_8(pci_config_data + (offset&3), val); 105} 106 107void cpc700_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, 108 unsigned char offset, unsigned short *val) 109{ 110 out_le32(pci_config_address, 111 (((bus & 0xff)<<16) | (dev_fn<<8) | (offset&0xfc) | 0x80000000)); 112 113 *val= in_le16((unsigned short *)(pci_config_data + (offset&3))); 114} 115 116void cpc700_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, 117 unsigned char offset, unsigned short val) 118{ 119 out_le32(pci_config_address, 120 (((bus & 0xff)<<16) | (dev_fn<<8) | (offset&0xfc) | 0x80000000)); 121 122 out_le16((unsigned short *)(pci_config_data + (offset&3)), val); 123} 124 125void cpc700_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, 126 unsigned char offset, unsigned int *val) 127{ 128 out_le32(pci_config_address, 129 (((bus & 0xff)<<16) | (dev_fn<<8) | (offset&0xfc) | 0x80000000)); 130 131 *val= in_le32((unsigned *)pci_config_data); 132} 133 134void cpc700_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, 135 unsigned char offset, unsigned int val) 136{ 137 out_le32(pci_config_address, 138 (((bus & 0xff)<<16) | (dev_fn<<8) | (offset&0xfc) | 0x80000000)); 139 140 out_le32((unsigned *)pci_config_data, val); 141} 142 143unsigned long isa_io_base = SPRUCE_ISA_IO_BASE; 144 145#define PCNET32_WIO_RDP 0x10 146#define PCNET32_WIO_RAP 0x12 147#define PCNET32_WIO_RESET 0x14 148 149#define PCNET32_DWIO_RDP 0x10 150#define PCNET32_DWIO_RAP 0x14 151#define PCNET32_DWIO_RESET 0x18 152 153/* Processor interface config register access */ 154#define PIFCFGADDR 0xff500000 155#define PIFCFGDATA 0xff500004 156 157#define PLBMIFOPT 0x18 /* PLB Master Interface Options */ 158 159#define MEM_MBEN 0x24 160#define MEM_TYPE 0x28 161#define MEM_B1SA 0x3c 162#define MEM_B1EA 0x5c 163#define MEM_B2SA 0x40 164#define MEM_B2EA 0x60 165 166unsigned long 167decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum) 168{ 169 int timer = 0; 170 char *cp, ch; 171 172 int loop; 173 int csr0; 174 int csr_id; 175 volatile int *mem_addr = (int *)0xff500008; 176 volatile int *mem_data = (int *)0xff50000c; 177 int mem_size = 0; 178 unsigned long mem_mben; 179 unsigned long mem_type; 180 unsigned long mem_start; 181 unsigned long mem_end; 182 volatile int *pif_addr = (int *)0xff500000; 183 volatile int *pif_data = (int *)0xff500004; 184 int pci_devfn; 185 int found_multi = 0; 186 unsigned short vendor; 187 unsigned short device; 188 unsigned short command; 189 unsigned char header_type; 190 unsigned int bar0; 191 192#ifdef CONFIG_SERIAL_CONSOLE 193 /* Initialize the serial console port */ 194 com_port = serial_init(0, NULL); 195#endif 196 197 /* 198 * Gah, these firmware guys need to learn that hardware 199 * byte swapping is evil! Disable all hardware byte 200 * swapping so it doesn't hurt anyone. 201 */ 202 *pif_addr = PLBMIFOPT; 203 asm("sync"); 204 *pif_data = 0x00000000; 205 asm("sync"); 206 207 /* Get the size of memory from the memory controller. */ 208 *mem_addr = MEM_MBEN; 209 asm("sync"); 210 mem_mben = *mem_data; 211 asm("sync"); 212 for(loop = 0; loop < 1000; loop++); 213 214 *mem_addr = MEM_TYPE; 215 asm("sync"); 216 mem_type = *mem_data; 217 asm("sync"); 218 for(loop = 0; loop < 1000; loop++); 219 220 *mem_addr = MEM_TYPE; 221 /* Confirm bank 1 has DRAM memory */ 222 if ((mem_mben & 0x40000000) && 223 ((mem_type & 0x30000000) == 0x10000000)) { 224 *mem_addr = MEM_B1SA; 225 asm("sync"); 226 mem_start = *mem_data; 227 asm("sync"); 228 for(loop = 0; loop < 1000; loop++); 229 230 *mem_addr = MEM_B1EA; 231 asm("sync"); 232 mem_end = *mem_data; 233 asm("sync"); 234 for(loop = 0; loop < 1000; loop++); 235 236 mem_size = mem_end - mem_start + 0x100000; 237 } 238 239 /* Confirm bank 2 has DRAM memory */ 240 if ((mem_mben & 0x20000000) && 241 ((mem_type & 0xc000000) == 0x4000000)) { 242 *mem_addr = MEM_B2SA; 243 asm("sync"); 244 mem_start = *mem_data; 245 asm("sync"); 246 for(loop = 0; loop < 1000; loop++); 247 248 *mem_addr = MEM_B2EA; 249 asm("sync"); 250 mem_end = *mem_data; 251 asm("sync"); 252 for(loop = 0; loop < 1000; loop++); 253 254 mem_size += mem_end - mem_start + 0x100000; 255 } 256 257 /* Search out and turn off the PcNet ethernet boot device. */ 258 for (pci_devfn = 1; pci_devfn < 0xff; pci_devfn++) { 259 if (PCI_FUNC(pci_devfn) && !found_multi) 260 continue; 261 262 cpc700_pcibios_read_config_byte(0, pci_devfn, 263 PCI_HEADER_TYPE, &header_type); 264 265 if (!PCI_FUNC(pci_devfn)) 266 found_multi = header_type & 0x80; 267 268 cpc700_pcibios_read_config_word(0, pci_devfn, PCI_VENDOR_ID, 269 &vendor); 270 271 if (vendor != 0xffff) { 272 cpc700_pcibios_read_config_word(0, pci_devfn, 273 PCI_DEVICE_ID, &device); 274 275 /* If this PCI device is the Lance PCNet board then turn it off */ 276 if ((vendor == PCI_VENDOR_ID_AMD) && 277 (device == PCI_DEVICE_ID_AMD_LANCE)) { 278 279 /* Turn on I/O Space on the board. */ 280 cpc700_pcibios_read_config_word(0, pci_devfn, 281 PCI_COMMAND, &command); 282 command |= 0x1; 283 cpc700_pcibios_write_config_word(0, pci_devfn, 284 PCI_COMMAND, command); 285 286 /* Get the I/O space address */ 287 cpc700_pcibios_read_config_dword(0, pci_devfn, 288 PCI_BASE_ADDRESS_0, &bar0); 289 bar0 &= 0xfffffffe; 290 291 /* Reset the PCNet Board */ 292 inl (bar0+PCNET32_DWIO_RESET); 293 inw (bar0+PCNET32_WIO_RESET); 294 295 /* First do a work oriented read of csr0. If the value is 296 * 4 then this is the correct mode to access the board. 297 * If not try a double word ortiented read. 298 */ 299 outw(0, bar0 + PCNET32_WIO_RAP); 300 csr0 = inw(bar0 + PCNET32_WIO_RDP); 301 302 if (csr0 == 4) { 303 /* Check the Chip id register */ 304 outw(88, bar0 + PCNET32_WIO_RAP); 305 csr_id = inw(bar0 + PCNET32_WIO_RDP); 306 307 if (csr_id) { 308 /* This is the valid mode - set the stop bit */ 309 outw(0, bar0 + PCNET32_WIO_RAP); 310 outw(csr0, bar0 + PCNET32_WIO_RDP); 311 } 312 } else { 313 outl(0, bar0 + PCNET32_DWIO_RAP); 314 csr0 = inl(bar0 + PCNET32_DWIO_RDP); 315 if (csr0 == 4) { 316 /* Check the Chip id register */ 317 outl(88, bar0 + PCNET32_WIO_RAP); 318 csr_id = inl(bar0 + PCNET32_WIO_RDP); 319 320 if (csr_id) { 321 /* This is the valid mode - set the stop bit*/ 322 outl(0, bar0 + PCNET32_WIO_RAP); 323 outl(csr0, bar0 + PCNET32_WIO_RDP); 324 } 325 } 326 } 327 } 328 } 329 } 330 331 /* assume the chunk below 8M is free */ 332 end_avail = (char *)0x00800000; 333 334 /* 335 * We link ourself to 0x00800000. When we run, we relocate 336 * ourselves there. So we just need __image_begin for the 337 * start. -- Tom 338 */ 339 zimage_start = (char *)(unsigned long)(&__image_begin); 340 zimage_size = (unsigned long)(&__image_end) - 341 (unsigned long)(&__image_begin); 342 343 initrd_size = (unsigned long)(&__ramdisk_end) - 344 (unsigned long)(&__ramdisk_begin); 345 346 /* 347 * The zImage and initrd will be between start and _end, so they've 348 * already been moved once. We're good to go now. -- Tom 349 */ 350 avail_ram = (char *)PAGE_ALIGN((unsigned long)_end); 351 puts("zimage at: "); puthex((unsigned long)zimage_start); 352 puts(" "); puthex((unsigned long)(zimage_size+zimage_start)); 353 puts("\n"); 354 355 if ( initrd_size ) { 356 puts("initrd at: "); 357 puthex((unsigned long)(&__ramdisk_begin)); 358 puts(" "); puthex((unsigned long)(&__ramdisk_end));puts("\n"); 359 } 360 361 avail_ram = (char *)0x00400000; 362 end_avail = (char *)0x00800000; 363 puts("avail ram: "); puthex((unsigned long)avail_ram); puts(" "); 364 puthex((unsigned long)end_avail); puts("\n"); 365 366 /* Display standard Linux/PPC boot prompt for kernel args */ 367 puts("\nLinux/PPC load: "); 368 cp = cmd_line; 369 memcpy (cmd_line, cmd_preset, sizeof(cmd_preset)); 370 while ( *cp ) putc(*cp++); 371 while (timer++ < 5*1000) { 372 if (tstc()) { 373 while ((ch = getc()) != '\n' && ch != '\r') { 374 if (ch == '\b') { 375 if (cp != cmd_line) { 376 cp--; 377 puts("\b \b"); 378 } 379 } else { 380 *cp++ = ch; 381 putc(ch); 382 } 383 } 384 break; /* Exit 'timer' loop */ 385 } 386 udelay(1000); /* 1 msec */ 387 } 388 *cp = 0; 389 puts("\n"); 390 391 puts("Uncompressing Linux..."); 392 393 gunzip(0, 0x400000, zimage_start, &zimage_size); 394 395 puts("done.\n"); 396 397 { 398 struct bi_record *rec; 399 400 rec = (struct bi_record *)_ALIGN((ulong)zimage_size + 401 (1<<20)-1,(1<<20)); 402 403 rec->tag = BI_FIRST; 404 rec->size = sizeof(struct bi_record); 405 rec = (struct bi_record *)((unsigned long)rec + rec->size); 406 407 rec->tag = BI_BOOTLOADER_ID; 408 memcpy( (void *)rec->data, "spruceboot", 11); 409 rec->size = sizeof(struct bi_record) + 10 + 1; 410 rec = (struct bi_record *)((unsigned long)rec + rec->size); 411 412 rec->tag = BI_MEMSIZE; 413 rec->data[0] = mem_size; 414 rec->size = sizeof(struct bi_record) + sizeof(unsigned long); 415 rec = (struct bi_record *)((unsigned long)rec + rec->size); 416 417 rec->tag = BI_CMD_LINE; 418 memcpy( (char *)rec->data, cmd_line, strlen(cmd_line)+1); 419 rec->size = sizeof(struct bi_record) + strlen(cmd_line) + 1; 420 rec = (struct bi_record *)((ulong)rec + rec->size); 421 422 if ( initrd_size ) { 423 rec->tag = BI_INITRD; 424 rec->data[0] = (unsigned long)(&__ramdisk_begin); 425 rec->data[1] = initrd_size; 426 rec->size = sizeof(struct bi_record) + 2 * 427 sizeof(unsigned long); 428 rec = (struct bi_record *)((unsigned long)rec + 429 rec->size); 430 } 431 432 rec->tag = BI_LAST; 433 rec->size = sizeof(struct bi_record); 434 rec = (struct bi_record *)((unsigned long)rec + rec->size); 435 } 436 437 puts("Now booting the kernel\n"); 438 439 return 0; 440} 441