blob: 60bad5c122cb4eb6a50eb81fd218ce7740a0ae86 [file] [log] [blame]
Stefan Reinauer38cd29e2009-08-11 21:28:25 +00001/******************************************************************************
2 * Copyright (c) 2004, 2008 IBM Corporation
3 * Copyright (c) 2008, 2009 Pattrick Hueper <phueper@hueper.net>
4 * All rights reserved.
5 * This program and the accompanying materials
6 * are made available under the terms of the BSD License
7 * which accompanies this distribution, and is available at
8 * http://www.opensource.org/licenses/bsd-license.php
9 *
10 * Contributors:
11 * IBM Corporation - initial implementation
12 *****************************************************************************/
13
14
15#include "device.h"
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000016#include "compat/rtas.h"
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000017#include <string.h>
18#include "debug.h"
19
20#include <device/device.h>
21#include <device/pci.h>
22#include <device/pci_ops.h>
23#include <device/resource.h>
24
25/* the device we are working with... */
26biosemu_device_t bios_device;
27//max. 6 BARs and 1 Exp.ROM plus CfgSpace and 3 legacy ranges
28translate_address_t translate_address_array[11];
29u8 taa_last_entry;
30
31typedef struct {
32 u8 info;
33 u8 bus;
34 u8 devfn;
35 u8 cfg_space_offset;
36 u64 address;
37 u64 size;
38} __attribute__ ((__packed__)) assigned_address_t;
39
40#ifdef CONFIG_PCI_OPTION_ROM_RUN_YABEL
41/* coreboot version */
42
43void
44biosemu_dev_get_addr_info(void)
45{
46 int taa_index = 0;
47 int i = 0;
48 struct resource *r;
49 u8 bus = bios_device.dev->bus->link;
50 u16 devfn = bios_device.dev->path.pci.devfn;
51
52 bios_device.bus = bus;
53 bios_device.devfn = devfn;
54
55 DEBUG_PRINTF("bus: %x, devfn: %x\n", bus, devfn);
56 for (i = 0; i < bios_device.dev->resources; i++) {
57 r = &bios_device.dev->resource[i];
58 translate_address_array[taa_index].info = r->flags;
59 translate_address_array[taa_index].bus = bus;
60 translate_address_array[taa_index].devfn = devfn;
61 translate_address_array[taa_index].cfg_space_offset =
62 r->index;
63 translate_address_array[taa_index].address = r->base;
64 translate_address_array[taa_index].size = r->size;
65 /* dont translate addresses... all addresses are 1:1 */
66 translate_address_array[taa_index].address_offset = 0;
67 taa_index++;
68 }
69 /* Expansion ROM */
70 translate_address_array[taa_index].info = IORESOURCE_MEM | IORESOURCE_READONLY;
71 translate_address_array[taa_index].bus = bus;
72 translate_address_array[taa_index].devfn = devfn;
73 translate_address_array[taa_index].cfg_space_offset = 0x30;
Stefan Reinauer775a7662010-01-19 21:14:24 +000074 translate_address_array[taa_index].address = bios_device.img_addr;
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000075 translate_address_array[taa_index].size = 0; /* TODO: do we need the size? */
76 /* dont translate addresses... all addresses are 1:1 */
77 translate_address_array[taa_index].address_offset = 0;
78 taa_index++;
79 /* legacy ranges if its a VGA card... */
80 if ((bios_device.dev->class & 0xFF0000) == 0x030000) {
81 DEBUG_PRINTF("%s: VGA device found, adding legacy resources... \n", __func__);
82 /* I/O 0x3B0-0x3BB */
83 translate_address_array[taa_index].info = IORESOURCE_FIXED | IORESOURCE_IO;
84 translate_address_array[taa_index].bus = bus;
85 translate_address_array[taa_index].devfn = devfn;
86 translate_address_array[taa_index].cfg_space_offset = 0;
87 translate_address_array[taa_index].address = 0x3b0;
88 translate_address_array[taa_index].size = 0xc;
89 /* dont translate addresses... all addresses are 1:1 */
90 translate_address_array[taa_index].address_offset = 0;
91 taa_index++;
92 /* I/O 0x3C0-0x3DF */
93 translate_address_array[taa_index].info = IORESOURCE_FIXED | IORESOURCE_IO;
94 translate_address_array[taa_index].bus = bus;
95 translate_address_array[taa_index].devfn = devfn;
96 translate_address_array[taa_index].cfg_space_offset = 0;
97 translate_address_array[taa_index].address = 0x3c0;
98 translate_address_array[taa_index].size = 0x20;
99 /* dont translate addresses... all addresses are 1:1 */
100 translate_address_array[taa_index].address_offset = 0;
101 taa_index++;
102 /* Mem 0xA0000-0xBFFFF */
103 translate_address_array[taa_index].info = IORESOURCE_FIXED | IORESOURCE_MEM;
104 translate_address_array[taa_index].bus = bus;
105 translate_address_array[taa_index].devfn = devfn;
106 translate_address_array[taa_index].cfg_space_offset = 0;
107 translate_address_array[taa_index].address = 0xa0000;
108 translate_address_array[taa_index].size = 0x20000;
109 /* dont translate addresses... all addresses are 1:1 */
110 translate_address_array[taa_index].address_offset = 0;
111 taa_index++;
112 }
113 // store last entry index of translate_address_array
114 taa_last_entry = taa_index - 1;
115#ifdef CONFIG_DEBUG
116 //dump translate_address_array
117 printf("translate_address_array: \n");
118 translate_address_t ta;
119 for (i = 0; i <= taa_last_entry; i++) {
120 ta = translate_address_array[i];
121 printf
122 ("%d: info: %08lx bus: %02x devfn: %02x cfg_space_offset: %02x\n\taddr: %016llx\n\toffs: %016llx\n\tsize: %016llx\n",
123 i, ta.info, ta.bus, ta.devfn, ta.cfg_space_offset,
124 ta.address, ta.address_offset, ta.size);
125 }
126#endif
127}
128#else
129// use translate_address_dev and get_puid from net-snk's net_support.c
130void translate_address_dev(u64 *, phandle_t);
131u64 get_puid(phandle_t node);
132
133
134// scan all adresses assigned to the device ("assigned-addresses" and "reg")
135// store in translate_address_array for faster translation using dev_translate_address
136void
137biosemu_dev_get_addr_info(void)
138{
139 // get bus/dev/fn from assigned-addresses
140 int32_t len;
141 //max. 6 BARs and 1 Exp.ROM plus CfgSpace and 3 legacy ranges
142 assigned_address_t buf[11];
143 len =
144 of_getprop(bios_device.phandle, "assigned-addresses", buf,
145 sizeof(buf));
146 bios_device.bus = buf[0].bus;
147 bios_device.devfn = buf[0].devfn;
148 DEBUG_PRINTF("bus: %x, devfn: %x\n", bios_device.bus,
149 bios_device.devfn);
150 //store address translations for all assigned-addresses and regs in
151 //translate_address_array for faster translation later on...
152 int i = 0;
153 // index to insert data into translate_address_array
154 int taa_index = 0;
155 u64 address_offset;
156 for (i = 0; i < (len / sizeof(assigned_address_t)); i++, taa_index++) {
157 //copy all info stored in assigned-addresses
158 translate_address_array[taa_index].info = buf[i].info;
159 translate_address_array[taa_index].bus = buf[i].bus;
160 translate_address_array[taa_index].devfn = buf[i].devfn;
161 translate_address_array[taa_index].cfg_space_offset =
162 buf[i].cfg_space_offset;
163 translate_address_array[taa_index].address = buf[i].address;
164 translate_address_array[taa_index].size = buf[i].size;
165 // translate first address and store it as address_offset
166 address_offset = buf[i].address;
167 translate_address_dev(&address_offset, bios_device.phandle);
168 translate_address_array[taa_index].address_offset =
169 address_offset - buf[i].address;
170 }
171 //get "reg" property
172 len = of_getprop(bios_device.phandle, "reg", buf, sizeof(buf));
173 for (i = 0; i < (len / sizeof(assigned_address_t)); i++) {
174 if ((buf[i].size == 0) || (buf[i].cfg_space_offset != 0)) {
175 // we dont care for ranges with size 0 and
176 // BARs and Expansion ROM must be in assigned-addresses... so in reg
177 // we only look for those without config space offset set...
178 // i.e. the legacy ranges
179 continue;
180 }
181 //copy all info stored in assigned-addresses
182 translate_address_array[taa_index].info = buf[i].info;
183 translate_address_array[taa_index].bus = buf[i].bus;
184 translate_address_array[taa_index].devfn = buf[i].devfn;
185 translate_address_array[taa_index].cfg_space_offset =
186 buf[i].cfg_space_offset;
187 translate_address_array[taa_index].address = buf[i].address;
188 translate_address_array[taa_index].size = buf[i].size;
189 // translate first address and store it as address_offset
190 address_offset = buf[i].address;
191 translate_address_dev(&address_offset, bios_device.phandle);
192 translate_address_array[taa_index].address_offset =
193 address_offset - buf[i].address;
194 taa_index++;
195 }
196 // store last entry index of translate_address_array
197 taa_last_entry = taa_index - 1;
198#ifdef CONFIG_DEBUG
199 //dump translate_address_array
200 printf("translate_address_array: \n");
201 translate_address_t ta;
202 for (i = 0; i <= taa_last_entry; i++) {
203 ta = translate_address_array[i];
204 printf
205 ("%d: %02x%02x%02x%02x\n\taddr: %016llx\n\toffs: %016llx\n\tsize: %016llx\n",
206 i, ta.info, ta.bus, ta.devfn, ta.cfg_space_offset,
207 ta.address, ta.address_offset, ta.size);
208 }
209#endif
210}
211#endif
212
213// to simulate accesses to legacy VGA Memory (0xA0000-0xBFFFF)
214// we look for the first prefetchable memory BAR, if no prefetchable BAR found,
215// we use the first memory BAR
216// dev_translate_addr will translate accesses to the legacy VGA Memory into the found vmem BAR
217void
218biosemu_dev_find_vmem_addr(void)
219{
220 int i = 0;
221 translate_address_t ta;
222 s8 tai_np = -1, tai_p = -1; // translate_address_array index for non-prefetchable and prefetchable memory
223 //search backwards to find first entry
224 for (i = taa_last_entry; i >= 0; i--) {
225 ta = translate_address_array[i];
226 if ((ta.cfg_space_offset >= 0x10)
227 && (ta.cfg_space_offset <= 0x24)) {
228 //only BARs
229 if ((ta.info & 0x03) >= 0x02) {
230 //32/64bit memory
231 tai_np = i;
232 if ((ta.info & 0x40) != 0) {
233 // prefetchable
234 tai_p = i;
235 }
236 }
237 }
238 }
239 if (tai_p != -1) {
240 ta = translate_address_array[tai_p];
241 bios_device.vmem_addr = ta.address;
242 bios_device.vmem_size = ta.size;
243 DEBUG_PRINTF
244 ("%s: Found prefetchable Virtual Legacy Memory BAR: %llx, size: %llx\n",
245 __func__, bios_device.vmem_addr,
246 bios_device.vmem_size);
247 } else if (tai_np != -1) {
248 ta = translate_address_array[tai_np];
249 bios_device.vmem_addr = ta.address;
250 bios_device.vmem_size = ta.size;
251 DEBUG_PRINTF
252 ("%s: Found non-prefetchable Virtual Legacy Memory BAR: %llx, size: %llx",
253 __func__, bios_device.vmem_addr,
254 bios_device.vmem_size);
255 }
256 // disable vmem
257 //bios_device.vmem_size = 0;
258}
259
260#ifndef CONFIG_PCI_OPTION_ROM_RUN_YABEL
261void
262biosemu_dev_get_puid(void)
263{
264 // get puid
265 bios_device.puid = get_puid(bios_device.phandle);
266 DEBUG_PRINTF("puid: 0x%llx\n", bios_device.puid);
267}
268#endif
269
270void
271biosemu_dev_get_device_vendor_id(void)
272{
273
274 u32 pci_config_0;
275#ifdef CONFIG_PCI_OPTION_ROM_RUN_YABEL
276 pci_config_0 = pci_read_config32(bios_device.dev, 0x0);
277#else
278 pci_config_0 =
279 rtas_pci_config_read(bios_device.puid, 4, bios_device.bus,
280 bios_device.devfn, 0x0);
281#endif
282 bios_device.pci_device_id =
283 (u16) ((pci_config_0 & 0xFFFF0000) >> 16);
284 bios_device.pci_vendor_id = (u16) (pci_config_0 & 0x0000FFFF);
285 DEBUG_PRINTF("PCI Device ID: %04x, PCI Vendor ID: %x\n",
286 bios_device.pci_device_id, bios_device.pci_vendor_id);
287}
288
289/* Check whether the device has a valid Expansion ROM and search the PCI Data
290 * Structure and any Expansion ROM Header (using dev_scan_exp_header()) for
291 * needed information. If the rom_addr parameter is != 0, it is the address of
292 * the Expansion ROM image and will be used, if it is == 0, the Expansion ROM
293 * BAR address will be used.
294 */
295u8
296biosemu_dev_check_exprom(unsigned long rom_base_addr)
297{
298 int i = 0;
299 translate_address_t ta;
300 u16 pci_ds_offset;
301 pci_data_struct_t pci_ds;
302 if (rom_base_addr == 0) {
303 // check for ExpROM Address (Offset 30) in taa
304 for (i = 0; i <= taa_last_entry; i++) {
305 ta = translate_address_array[i];
306 if (ta.cfg_space_offset == 0x30) {
307 //translated address
308 rom_base_addr = ta.address + ta.address_offset;
309 break;
310 }
311 }
312 }
313 /* In the ROM there could be multiple Expansion ROM Images... start
314 * searching them for an x86 image.
315 */
316 do {
317 if (rom_base_addr == 0) {
318 printf("Error: no Expansion ROM address found!\n");
319 return -1;
320 }
321 set_ci();
322 u16 rom_signature = in16le((void *) rom_base_addr);
323 clr_ci();
324 if (rom_signature != 0xaa55) {
325 printf
326 ("Error: invalid Expansion ROM signature: %02x!\n",
327 *((u16 *) rom_base_addr));
328 return -1;
329 }
330 set_ci();
331 // at offset 0x18 is the (16bit little-endian) pointer to the PCI Data Structure
332 pci_ds_offset = in16le((void *) (rom_base_addr + 0x18));
333 //copy the PCI Data Structure
334 memcpy(&pci_ds, (void *) (rom_base_addr + pci_ds_offset),
335 sizeof(pci_ds));
336 clr_ci();
337#ifdef CONFIG_DEBUG
338 DEBUG_PRINTF("PCI Data Structure @%lx:\n",
339 rom_base_addr + pci_ds_offset);
340 dump((void *) &pci_ds, sizeof(pci_ds));
341#endif
342 if (strncmp((const char *) pci_ds.signature, "PCIR", 4) != 0) {
343 printf("Invalid PCI Data Structure found!\n");
344 break;
345 }
346 //little-endian conversion
347 pci_ds.vendor_id = in16le(&pci_ds.vendor_id);
348 pci_ds.device_id = in16le(&pci_ds.device_id);
349 pci_ds.img_length = in16le(&pci_ds.img_length);
350 pci_ds.pci_ds_length = in16le(&pci_ds.pci_ds_length);
351 if (pci_ds.vendor_id != bios_device.pci_vendor_id) {
352 printf
353 ("Image has invalid Vendor ID: %04x, expected: %04x\n",
354 pci_ds.vendor_id, bios_device.pci_vendor_id);
355 break;
356 }
357 if (pci_ds.device_id != bios_device.pci_device_id) {
358 printf
359 ("Image has invalid Device ID: %04x, expected: %04x\n",
360 pci_ds.device_id, bios_device.pci_device_id);
361 break;
362 }
363 DEBUG_PRINTF("Image Length: %d\n", pci_ds.img_length * 512);
364 DEBUG_PRINTF("Image Code Type: %d\n", pci_ds.code_type);
365 if (pci_ds.code_type == 0) {
366 //x86 image
367 //store image address and image length in bios_device struct
368 bios_device.img_addr = rom_base_addr;
369 bios_device.img_size = pci_ds.img_length * 512;
370 // we found the image, exit the loop
371 break;
372 } else {
373 // no x86 image, check next image (if any)
374 rom_base_addr += pci_ds.img_length * 512;
375 }
376 if ((pci_ds.indicator & 0x80) == 0x80) {
377 //last image found, exit the loop
378 DEBUG_PRINTF("Last PCI Expansion ROM Image found.\n");
379 break;
380 }
381 }
382 while (bios_device.img_addr == 0);
383 // in case we did not find a valid x86 Expansion ROM Image
384 if (bios_device.img_addr == 0) {
385 printf("Error: no valid x86 Expansion ROM Image found!\n");
386 return -1;
387 }
388 return 0;
389}
390
391u8
392biosemu_dev_init(struct device * device)
393{
394 u8 rval = 0;
395 //init bios_device struct
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000396 DEBUG_PRINTF("%s\n", __func__);
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000397 memset(&bios_device, 0, sizeof(bios_device));
398
399#ifndef CONFIG_PCI_OPTION_ROM_RUN_YABEL
400 bios_device.ihandle = of_open(device_name);
401 if (bios_device.ihandle == 0) {
402 DEBUG_PRINTF("%s is no valid device!\n", device_name);
403 return -1;
404 }
405 bios_device.phandle = of_finddevice(device_name);
406#else
407 bios_device.dev = device;
408#endif
409 biosemu_dev_get_addr_info();
410#ifndef CONFIG_PCI_OPTION_ROM_RUN_YABEL
411 biosemu_dev_find_vmem_addr();
412 biosemu_dev_get_puid();
413#endif
414 biosemu_dev_get_device_vendor_id();
415 return rval;
416}
417
418// translate address function using translate_address_array assembled
419// by dev_get_addr_info... MUCH faster than calling translate_address_dev
420// and accessing client interface for every translation...
421// returns: 0 if addr not found in translate_address_array, 1 if found.
422u8
423biosemu_dev_translate_address(unsigned long * addr)
424{
425 int i = 0;
426 translate_address_t ta;
427#ifndef CONFIG_PCI_OPTION_ROM_RUN_YABEL
428 /* we dont need this hack for coreboot... we can access legacy areas */
429 //check if it is an access to legacy VGA Mem... if it is, map the address
430 //to the vmem BAR and then translate it...
431 // (translation info provided by Ben Herrenschmidt)
432 // NOTE: the translation seems to only work for NVIDIA cards... but it is needed
433 // to make some NVIDIA cards work at all...
434 if ((bios_device.vmem_size > 0)
435 && ((*addr >= 0xA0000) && (*addr < 0xB8000))) {
436 *addr = (*addr - 0xA0000) * 4 + 2 + bios_device.vmem_addr;
437 }
438 if ((bios_device.vmem_size > 0)
439 && ((*addr >= 0xB8000) && (*addr < 0xC0000))) {
440 u8 shift = *addr & 1;
441 *addr &= 0xfffffffe;
442 *addr = (*addr - 0xB8000) * 4 + shift + bios_device.vmem_addr;
443 }
444#endif
445 for (i = 0; i <= taa_last_entry; i++) {
446 ta = translate_address_array[i];
447 if ((*addr >= ta.address) && (*addr <= (ta.address + ta.size))) {
448 *addr += ta.address_offset;
449 return 1;
450 }
451 }
452 return 0;
453}