1/* 2 * Misc. bootloader code for IBM Spruce reference platform 3 * 4 * Authors: Johnnie Peters <jpeters@mvista.com> 5 * Matt Porter <mporter@mvista.com> 6 * 7 * Derived from arch/ppc/boot/prep/misc.c 8 * 9 * 2000-2001 (c) MontaVista, Software, Inc. This file is licensed under 10 * the terms of the GNU General Public License version 2. This program 11 * is licensed "as is" without any warranty of any kind, whether express 12 * or implied. 13 */ 14 15#include <linux/types.h> 16#include <linux/pci.h> 17 18#include <asm/bootinfo.h> 19 20extern unsigned long decompress_kernel(unsigned long load_addr, int num_words, 21 unsigned long cksum); 22 23/* Define some important locations of the Spruce. */ 24#define SPRUCE_PCI_CONFIG_ADDR 0xfec00000 25#define SPRUCE_PCI_CONFIG_DATA 0xfec00004 26 27/* PCI configuration space access routines. */ 28unsigned int *pci_config_address = (unsigned int *)SPRUCE_PCI_CONFIG_ADDR; 29unsigned char *pci_config_data = (unsigned char *)SPRUCE_PCI_CONFIG_DATA; 30 31void cpc700_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, 32 unsigned char offset, unsigned char *val) 33{ 34 out_le32(pci_config_address, 35 (((bus & 0xff)<<16) | (dev_fn<<8) | (offset&0xfc) | 0x80000000)); 36 37 *val= (in_le32((unsigned *)pci_config_data) >> (8 * (offset & 3))) & 0xff; 38} 39 40void cpc700_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, 41 unsigned char offset, unsigned char val) 42{ 43 out_le32(pci_config_address, 44 (((bus & 0xff)<<16) | (dev_fn<<8) | (offset&0xfc) | 0x80000000)); 45 46 out_8(pci_config_data + (offset&3), val); 47} 48 49void cpc700_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, 50 unsigned char offset, unsigned short *val) 51{ 52 out_le32(pci_config_address, 53 (((bus & 0xff)<<16) | (dev_fn<<8) | (offset&0xfc) | 0x80000000)); 54 55 *val= in_le16((unsigned short *)(pci_config_data + (offset&3))); 56} 57 58void cpc700_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, 59 unsigned char offset, unsigned short val) 60{ 61 out_le32(pci_config_address, 62 (((bus & 0xff)<<16) | (dev_fn<<8) | (offset&0xfc) | 0x80000000)); 63 64 out_le16((unsigned short *)(pci_config_data + (offset&3)), val); 65} 66 67void cpc700_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, 68 unsigned char offset, unsigned int *val) 69{ 70 out_le32(pci_config_address, 71 (((bus & 0xff)<<16) | (dev_fn<<8) | (offset&0xfc) | 0x80000000)); 72 73 *val= in_le32((unsigned *)pci_config_data); 74} 75 76void cpc700_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, 77 unsigned char offset, unsigned int val) 78{ 79 out_le32(pci_config_address, 80 (((bus & 0xff)<<16) | (dev_fn<<8) | (offset&0xfc) | 0x80000000)); 81 82 out_le32((unsigned *)pci_config_data, val); 83} 84 85#define PCNET32_WIO_RDP 0x10 86#define PCNET32_WIO_RAP 0x12 87#define PCNET32_WIO_RESET 0x14 88 89#define PCNET32_DWIO_RDP 0x10 90#define PCNET32_DWIO_RAP 0x14 91#define PCNET32_DWIO_RESET 0x18 92 93/* Processor interface config register access */ 94#define PIFCFGADDR 0xff500000 95#define PIFCFGDATA 0xff500004 96 97#define PLBMIFOPT 0x18 /* PLB Master Interface Options */ 98 99#define MEM_MBEN 0x24 100#define MEM_TYPE 0x28 101#define MEM_B1SA 0x3c 102#define MEM_B1EA 0x5c 103#define MEM_B2SA 0x40 104#define MEM_B2EA 0x60 105 106unsigned long 107get_mem_size(void) 108{ 109 int loop; 110 unsigned long mem_size = 0; 111 unsigned long mem_mben; 112 unsigned long mem_type; 113 unsigned long mem_start; 114 unsigned long mem_end; 115 volatile int *mem_addr = (int *)0xff500008; 116 volatile int *mem_data = (int *)0xff50000c; 117 118 /* Get the size of memory from the memory controller. */ 119 *mem_addr = MEM_MBEN; 120 asm("sync"); 121 mem_mben = *mem_data; 122 asm("sync"); 123 for(loop = 0; loop < 1000; loop++); 124 125 *mem_addr = MEM_TYPE; 126 asm("sync"); 127 mem_type = *mem_data; 128 asm("sync"); 129 for(loop = 0; loop < 1000; loop++); 130 131 *mem_addr = MEM_TYPE; 132 /* Confirm bank 1 has DRAM memory */ 133 if ((mem_mben & 0x40000000) && 134 ((mem_type & 0x30000000) == 0x10000000)) { 135 *mem_addr = MEM_B1SA; 136 asm("sync"); 137 mem_start = *mem_data; 138 asm("sync"); 139 for(loop = 0; loop < 1000; loop++); 140 141 *mem_addr = MEM_B1EA; 142 asm("sync"); 143 mem_end = *mem_data; 144 asm("sync"); 145 for(loop = 0; loop < 1000; loop++); 146 147 mem_size = mem_end - mem_start + 0x100000; 148 } 149 150 /* Confirm bank 2 has DRAM memory */ 151 if ((mem_mben & 0x20000000) && 152 ((mem_type & 0xc000000) == 0x4000000)) { 153 *mem_addr = MEM_B2SA; 154 asm("sync"); 155 mem_start = *mem_data; 156 asm("sync"); 157 for(loop = 0; loop < 1000; loop++); 158 159 *mem_addr = MEM_B2EA; 160 asm("sync"); 161 mem_end = *mem_data; 162 asm("sync"); 163 for(loop = 0; loop < 1000; loop++); 164 165 mem_size += mem_end - mem_start + 0x100000; 166 } 167 return mem_size; 168} 169 170unsigned long 171load_kernel(unsigned long load_addr, int num_words, unsigned long cksum, 172 void *ign1, void *ign2) 173{ 174 int csr0; 175 int csr_id; 176 int pci_devfn; 177 int found_multi = 0; 178 unsigned short vendor; 179 unsigned short device; 180 unsigned short command; 181 unsigned char header_type; 182 unsigned int bar0; 183 volatile int *pif_addr = (int *)0xff500000; 184 volatile int *pif_data = (int *)0xff500004; 185 186 /* 187 * Gah, these firmware guys need to learn that hardware 188 * byte swapping is evil! Disable all hardware byte 189 * swapping so it doesn't hurt anyone. 190 */ 191 *pif_addr = PLBMIFOPT; 192 asm("sync"); 193 *pif_data = 0x00000000; 194 asm("sync"); 195 196 /* Search out and turn off the PcNet ethernet boot device. */ 197 for (pci_devfn = 1; pci_devfn < 0xff; pci_devfn++) { 198 if (PCI_FUNC(pci_devfn) && !found_multi) 199 continue; 200 201 cpc700_pcibios_read_config_byte(0, pci_devfn, 202 PCI_HEADER_TYPE, &header_type); 203 204 if (!PCI_FUNC(pci_devfn)) 205 found_multi = header_type & 0x80; 206 207 cpc700_pcibios_read_config_word(0, pci_devfn, PCI_VENDOR_ID, 208 &vendor); 209 210 if (vendor != 0xffff) { 211 cpc700_pcibios_read_config_word(0, pci_devfn, 212 PCI_DEVICE_ID, &device); 213 214 /* If this PCI device is the Lance PCNet board then turn it off */ 215 if ((vendor == PCI_VENDOR_ID_AMD) && 216 (device == PCI_DEVICE_ID_AMD_LANCE)) { 217 218 /* Turn on I/O Space on the board. */ 219 cpc700_pcibios_read_config_word(0, pci_devfn, 220 PCI_COMMAND, &command); 221 command |= 0x1; 222 cpc700_pcibios_write_config_word(0, pci_devfn, 223 PCI_COMMAND, command); 224 225 /* Get the I/O space address */ 226 cpc700_pcibios_read_config_dword(0, pci_devfn, 227 PCI_BASE_ADDRESS_0, &bar0); 228 bar0 &= 0xfffffffe; 229 230 /* Reset the PCNet Board */ 231 inl (bar0+PCNET32_DWIO_RESET); 232 inw (bar0+PCNET32_WIO_RESET); 233 234 /* First do a work oriented read of csr0. If the value is 235 * 4 then this is the correct mode to access the board. 236 * If not try a double word ortiented read. 237 */ 238 outw(0, bar0 + PCNET32_WIO_RAP); 239 csr0 = inw(bar0 + PCNET32_WIO_RDP); 240 241 if (csr0 == 4) { 242 /* Check the Chip id register */ 243 outw(88, bar0 + PCNET32_WIO_RAP); 244 csr_id = inw(bar0 + PCNET32_WIO_RDP); 245 246 if (csr_id) { 247 /* This is the valid mode - set the stop bit */ 248 outw(0, bar0 + PCNET32_WIO_RAP); 249 outw(csr0, bar0 + PCNET32_WIO_RDP); 250 } 251 } else { 252 outl(0, bar0 + PCNET32_DWIO_RAP); 253 csr0 = inl(bar0 + PCNET32_DWIO_RDP); 254 if (csr0 == 4) { 255 /* Check the Chip id register */ 256 outl(88, bar0 + PCNET32_WIO_RAP); 257 csr_id = inl(bar0 + PCNET32_WIO_RDP); 258 259 if (csr_id) { 260 /* This is the valid mode - set the stop bit*/ 261 outl(0, bar0 + PCNET32_WIO_RAP); 262 outl(csr0, bar0 + PCNET32_WIO_RDP); 263 } 264 } 265 } 266 } 267 } 268 } 269 270 return decompress_kernel(load_addr, num_words, cksum); 271} 272