source: mainline/uspace/drv/pciintel/pci.c@ ad4562c2

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since ad4562c2 was eb1a2f4, checked in by Vojtech Horky <vojtechhorky@…>, 15 years ago

Merge mainline changes (DDF refactoring)

This merge includes DDF refactoring that brought multifunctional devices
(i.e. ddf_dev_t and ddf_fun_t). Please, see ticket #295 at HelenOS
upstream Trac.

The conflicts themselves were easy to solve (merely several renamings).

Changes to USB subsystem:

  • drivers uses ddf_dev_t and ddf_fun_t
  • different signatures of many library functions
  • several hacks around communication with parent device (now the communication is clearer and somehow what we have now is hack about other hacks)
    • will repair and clean later
  • maybe added some extra debugging messages (the diff has about 240K, and I admit I have no energy to double check that)

WARNING:

  • the diff is VERY long, recommended is viewing partial diffs of the merge (i.e. merges in mainline branch that lead to the parent one)
  • merging with your branches might involve huge renamings, sorry, no other way is possible

BUGS:

  • hub driver will not work (no function created)

GOOD NEWS:

  • QEMU keyboard seems to work with QEMU 0.13 and 0.14
  • we are up-to-date with mainline again
  • Property mode set to 100644
File size: 15.6 KB
Line 
1/*
2 * Copyright (c) 2010 Lenka Trochtova
3 * Copyright (c) 2011 Jiri Svoboda
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN 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 USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * 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
30/**
31 * @defgroup pciintel pci bus driver for intel method 1.
32 * @brief HelenOS root pci bus driver for intel method 1.
33 * @{
34 */
35
36/** @file
37 */
38
39#include <assert.h>
40#include <stdio.h>
41#include <errno.h>
42#include <bool.h>
43#include <fibril_synch.h>
44#include <str.h>
45#include <ctype.h>
46#include <macros.h>
47#include <str_error.h>
48
49#include <ddf/driver.h>
50#include <devman.h>
51#include <ipc/devman.h>
52#include <ipc/dev_iface.h>
53#include <ipc/irc.h>
54#include <ipc/ns.h>
55#include <ipc/services.h>
56#include <sysinfo.h>
57#include <ops/hw_res.h>
58#include <device/hw_res.h>
59#include <ddi.h>
60#include <libarch/ddi.h>
61
62#include "pci.h"
63
64#define NAME "pciintel"
65
66#define CONF_ADDR(bus, dev, fn, reg) \
67 ((1 << 31) | (bus << 16) | (dev << 11) | (fn << 8) | (reg & ~3))
68
69/** Obtain PCI function soft-state from DDF function node */
70#define PCI_FUN(fnode) ((pci_fun_t *) (fnode)->driver_data)
71
72/** Obtain PCI bus soft-state from DDF device node */
73#define PCI_BUS(dnode) ((pci_bus_t *) (dnode)->driver_data)
74
75/** Obtain PCI bus soft-state from function soft-state */
76#define PCI_BUS_FROM_FUN(fun) ((fun)->busptr)
77
78static hw_resource_list_t *pciintel_get_resources(ddf_fun_t *fnode)
79{
80 pci_fun_t *fun = PCI_FUN(fnode);
81
82 if (fun == NULL)
83 return NULL;
84 return &fun->hw_resources;
85}
86
87static bool pciintel_enable_interrupt(ddf_fun_t *fnode)
88{
89 /* This is an old ugly way, copied from ne2000 driver */
90 assert(fnode);
91 pci_fun_t *dev_data = (pci_fun_t *) fnode->driver_data;
92
93 sysarg_t apic;
94 sysarg_t i8259;
95 int irc_phone = -1;
96 int irc_service = 0;
97
98 if ((sysinfo_get_value("apic", &apic) == EOK) && (apic)) {
99 irc_service = SERVICE_APIC;
100 } else if ((sysinfo_get_value("i8259", &i8259) == EOK) && (i8259)) {
101 irc_service = SERVICE_I8259;
102 }
103
104 if (irc_service) {
105 while (irc_phone < 0)
106 irc_phone = service_connect_blocking(irc_service, 0, 0);
107 } else {
108 return false;
109 }
110
111 size_t i;
112 for (i = 0; i < dev_data->hw_resources.count; i++) {
113 if (dev_data->hw_resources.resources[i].type == INTERRUPT) {
114 int irq = dev_data->hw_resources.resources[i].res.interrupt.irq;
115 async_msg_1(irc_phone, IRC_ENABLE_INTERRUPT, irq);
116 }
117 }
118
119 async_hangup(irc_phone);
120 return true;
121}
122
123static hw_res_ops_t pciintel_hw_res_ops = {
124 &pciintel_get_resources,
125 &pciintel_enable_interrupt
126};
127
128static ddf_dev_ops_t pci_fun_ops;
129
130static int pci_add_device(ddf_dev_t *);
131
132/** PCI bus driver standard operations */
133static driver_ops_t pci_ops = {
134 .add_device = &pci_add_device
135};
136
137/** PCI bus driver structure */
138static driver_t pci_driver = {
139 .name = NAME,
140 .driver_ops = &pci_ops
141};
142
143static pci_bus_t *pci_bus_new(void)
144{
145 pci_bus_t *bus;
146
147 bus = (pci_bus_t *) calloc(1, sizeof(pci_bus_t));
148 if (bus == NULL)
149 return NULL;
150
151 fibril_mutex_initialize(&bus->conf_mutex);
152 return bus;
153}
154
155static void pci_bus_delete(pci_bus_t *bus)
156{
157 assert(bus != NULL);
158 free(bus);
159}
160
161static void pci_conf_read(pci_fun_t *fun, int reg, uint8_t *buf, size_t len)
162{
163 pci_bus_t *bus = PCI_BUS_FROM_FUN(fun);
164
165 fibril_mutex_lock(&bus->conf_mutex);
166
167 uint32_t conf_addr;
168 conf_addr = CONF_ADDR(fun->bus, fun->dev, fun->fn, reg);
169 void *addr = bus->conf_data_port + (reg & 3);
170
171 pio_write_32(bus->conf_addr_port, conf_addr);
172
173 switch (len) {
174 case 1:
175 buf[0] = pio_read_8(addr);
176 break;
177 case 2:
178 ((uint16_t *) buf)[0] = pio_read_16(addr);
179 break;
180 case 4:
181 ((uint32_t *) buf)[0] = pio_read_32(addr);
182 break;
183 }
184
185 fibril_mutex_unlock(&bus->conf_mutex);
186}
187
188static void pci_conf_write(pci_fun_t *fun, int reg, uint8_t *buf, size_t len)
189{
190 pci_bus_t *bus = PCI_BUS_FROM_FUN(fun);
191
192 fibril_mutex_lock(&bus->conf_mutex);
193
194 uint32_t conf_addr;
195 conf_addr = CONF_ADDR(fun->bus, fun->dev, fun->fn, reg);
196 void *addr = bus->conf_data_port + (reg & 3);
197
198 pio_write_32(bus->conf_addr_port, conf_addr);
199
200 switch (len) {
201 case 1:
202 pio_write_8(addr, buf[0]);
203 break;
204 case 2:
205 pio_write_16(addr, ((uint16_t *) buf)[0]);
206 break;
207 case 4:
208 pio_write_32(addr, ((uint32_t *) buf)[0]);
209 break;
210 }
211
212 fibril_mutex_unlock(&bus->conf_mutex);
213}
214
215uint8_t pci_conf_read_8(pci_fun_t *fun, int reg)
216{
217 uint8_t res;
218 pci_conf_read(fun, reg, &res, 1);
219 return res;
220}
221
222uint16_t pci_conf_read_16(pci_fun_t *fun, int reg)
223{
224 uint16_t res;
225 pci_conf_read(fun, reg, (uint8_t *) &res, 2);
226 return res;
227}
228
229uint32_t pci_conf_read_32(pci_fun_t *fun, int reg)
230{
231 uint32_t res;
232 pci_conf_read(fun, reg, (uint8_t *) &res, 4);
233 return res;
234}
235
236void pci_conf_write_8(pci_fun_t *fun, int reg, uint8_t val)
237{
238 pci_conf_write(fun, reg, (uint8_t *) &val, 1);
239}
240
241void pci_conf_write_16(pci_fun_t *fun, int reg, uint16_t val)
242{
243 pci_conf_write(fun, reg, (uint8_t *) &val, 2);
244}
245
246void pci_conf_write_32(pci_fun_t *fun, int reg, uint32_t val)
247{
248 pci_conf_write(fun, reg, (uint8_t *) &val, 4);
249}
250
251void pci_fun_create_match_ids(pci_fun_t *fun)
252{
253 char *match_id_str;
254 int rc;
255
256 asprintf(&match_id_str, "pci/ven=%04x&dev=%04x",
257 fun->vendor_id, fun->device_id);
258
259 if (match_id_str == NULL) {
260 printf(NAME ": out of memory creating match ID.\n");
261 return;
262 }
263
264 rc = ddf_fun_add_match_id(fun->fnode, match_id_str, 90);
265 if (rc != EOK) {
266 printf(NAME ": error adding match ID: %s\n",
267 str_error(rc));
268 }
269
270 /* TODO add more ids (with subsys ids, using class id etc.) */
271}
272
273void pci_add_range(pci_fun_t *fun, uint64_t range_addr, size_t range_size,
274 bool io)
275{
276 hw_resource_list_t *hw_res_list = &fun->hw_resources;
277 hw_resource_t *hw_resources = hw_res_list->resources;
278 size_t count = hw_res_list->count;
279
280 assert(hw_resources != NULL);
281 assert(count < PCI_MAX_HW_RES);
282
283 if (io) {
284 hw_resources[count].type = IO_RANGE;
285 hw_resources[count].res.io_range.address = range_addr;
286 hw_resources[count].res.io_range.size = range_size;
287 hw_resources[count].res.io_range.endianness = LITTLE_ENDIAN;
288 } else {
289 hw_resources[count].type = MEM_RANGE;
290 hw_resources[count].res.mem_range.address = range_addr;
291 hw_resources[count].res.mem_range.size = range_size;
292 hw_resources[count].res.mem_range.endianness = LITTLE_ENDIAN;
293 }
294
295 hw_res_list->count++;
296}
297
298/** Read the base address register (BAR) of the device and if it contains valid
299 * address add it to the devices hw resource list.
300 *
301 * @param fun PCI function
302 * @param addr The address of the BAR in the PCI configuration address space of
303 * the device
304 * @return The addr the address of the BAR which should be read next
305 */
306int pci_read_bar(pci_fun_t *fun, int addr)
307{
308 /* Value of the BAR */
309 uint32_t val, mask;
310 /* IO space address */
311 bool io;
312 /* 64-bit wide address */
313 bool addrw64;
314
315 /* Size of the io or memory range specified by the BAR */
316 size_t range_size;
317 /* Beginning of the io or memory range specified by the BAR */
318 uint64_t range_addr;
319
320 /* Get the value of the BAR. */
321 val = pci_conf_read_32(fun, addr);
322
323 io = (bool) (val & 1);
324 if (io) {
325 addrw64 = false;
326 } else {
327 switch ((val >> 1) & 3) {
328 case 0:
329 addrw64 = false;
330 break;
331 case 2:
332 addrw64 = true;
333 break;
334 default:
335 /* reserved, go to the next BAR */
336 return addr + 4;
337 }
338 }
339
340 /* Get the address mask. */
341 pci_conf_write_32(fun, addr, 0xffffffff);
342 mask = pci_conf_read_32(fun, addr);
343
344 /* Restore the original value. */
345 pci_conf_write_32(fun, addr, val);
346 val = pci_conf_read_32(fun, addr);
347
348 range_size = pci_bar_mask_to_size(mask);
349
350 if (addrw64) {
351 range_addr = ((uint64_t)pci_conf_read_32(fun, addr + 4) << 32) |
352 (val & 0xfffffff0);
353 } else {
354 range_addr = (val & 0xfffffff0);
355 }
356
357 if (range_addr != 0) {
358 printf(NAME ": function %s : ", fun->fnode->name);
359 printf("address = %" PRIx64, range_addr);
360 printf(", size = %x\n", (unsigned int) range_size);
361 }
362
363 pci_add_range(fun, range_addr, range_size, io);
364
365 if (addrw64)
366 return addr + 8;
367
368 return addr + 4;
369}
370
371void pci_add_interrupt(pci_fun_t *fun, int irq)
372{
373 hw_resource_list_t *hw_res_list = &fun->hw_resources;
374 hw_resource_t *hw_resources = hw_res_list->resources;
375 size_t count = hw_res_list->count;
376
377 assert(NULL != hw_resources);
378 assert(count < PCI_MAX_HW_RES);
379
380 hw_resources[count].type = INTERRUPT;
381 hw_resources[count].res.interrupt.irq = irq;
382
383 hw_res_list->count++;
384
385 printf(NAME ": function %s uses irq %x.\n", fun->fnode->name, irq);
386}
387
388void pci_read_interrupt(pci_fun_t *fun)
389{
390 uint8_t irq = pci_conf_read_8(fun, PCI_BRIDGE_INT_LINE);
391 if (irq != 0xff)
392 pci_add_interrupt(fun, irq);
393}
394
395/** Enumerate (recursively) and register the devices connected to a pci bus.
396 *
397 * @param bus Host-to-PCI bridge
398 * @param bus_num Bus number
399 */
400void pci_bus_scan(pci_bus_t *bus, int bus_num)
401{
402 ddf_fun_t *fnode;
403 pci_fun_t *fun;
404
405 int child_bus = 0;
406 int dnum, fnum;
407 bool multi;
408 uint8_t header_type;
409
410 fun = pci_fun_new(bus);
411
412 for (dnum = 0; dnum < 32; dnum++) {
413 multi = true;
414 for (fnum = 0; multi && fnum < 8; fnum++) {
415 pci_fun_init(fun, bus_num, dnum, fnum);
416 fun->vendor_id = pci_conf_read_16(fun,
417 PCI_VENDOR_ID);
418 fun->device_id = pci_conf_read_16(fun,
419 PCI_DEVICE_ID);
420 if (fun->vendor_id == 0xffff) {
421 /*
422 * The device is not present, go on scanning the
423 * bus.
424 */
425 if (fnum == 0)
426 break;
427 else
428 continue;
429 }
430
431 header_type = pci_conf_read_8(fun, PCI_HEADER_TYPE);
432 if (fnum == 0) {
433 /* Is the device multifunction? */
434 multi = header_type >> 7;
435 }
436 /* Clear the multifunction bit. */
437 header_type = header_type & 0x7F;
438
439 char *fun_name = pci_fun_create_name(fun);
440 if (fun_name == NULL) {
441 printf(NAME ": out of memory.\n");
442 return;
443 }
444
445 fnode = ddf_fun_create(bus->dnode, fun_inner, fun_name);
446 if (fnode == NULL) {
447 printf(NAME ": error creating function.\n");
448 return;
449 }
450
451 free(fun_name);
452 fun->fnode = fnode;
453
454 pci_alloc_resource_list(fun);
455 pci_read_bars(fun);
456 pci_read_interrupt(fun);
457
458 fnode->ops = &pci_fun_ops;
459 fnode->driver_data = fun;
460
461 printf(NAME ": adding new function %s.\n",
462 fnode->name);
463
464 pci_fun_create_match_ids(fun);
465
466 if (ddf_fun_bind(fnode) != EOK) {
467 pci_clean_resource_list(fun);
468 clean_match_ids(&fnode->match_ids);
469 free((char *) fnode->name);
470 fnode->name = NULL;
471 continue;
472 }
473
474 if (header_type == PCI_HEADER_TYPE_BRIDGE ||
475 header_type == PCI_HEADER_TYPE_CARDBUS) {
476 child_bus = pci_conf_read_8(fun,
477 PCI_BRIDGE_SEC_BUS_NUM);
478 printf(NAME ": device is pci-to-pci bridge, "
479 "secondary bus number = %d.\n", bus_num);
480 if (child_bus > bus_num)
481 pci_bus_scan(bus, child_bus);
482 }
483
484 fun = pci_fun_new(bus);
485 }
486 }
487
488 if (fun->vendor_id == 0xffff) {
489 /* Free the auxiliary function structure. */
490 pci_fun_delete(fun);
491 }
492}
493
494static int pci_add_device(ddf_dev_t *dnode)
495{
496 pci_bus_t *bus = NULL;
497 ddf_fun_t *ctl = NULL;
498 bool got_res = false;
499 int rc;
500
501 printf(NAME ": pci_add_device\n");
502 dnode->parent_phone = -1;
503
504 bus = pci_bus_new();
505 if (bus == NULL) {
506 printf(NAME ": pci_add_device allocation failed.\n");
507 rc = ENOMEM;
508 goto fail;
509 }
510 bus->dnode = dnode;
511 dnode->driver_data = bus;
512
513 dnode->parent_phone = devman_parent_device_connect(dnode->handle,
514 IPC_FLAG_BLOCKING);
515 if (dnode->parent_phone < 0) {
516 printf(NAME ": pci_add_device failed to connect to the "
517 "parent's driver.\n");
518 rc = dnode->parent_phone;
519 goto fail;
520 }
521
522 hw_resource_list_t hw_resources;
523
524 rc = hw_res_get_resource_list(dnode->parent_phone, &hw_resources);
525 if (rc != EOK) {
526 printf(NAME ": pci_add_device failed to get hw resources for "
527 "the device.\n");
528 goto fail;
529 }
530 got_res = true;
531
532 printf(NAME ": conf_addr = %" PRIx64 ".\n",
533 hw_resources.resources[0].res.io_range.address);
534
535 assert(hw_resources.count > 0);
536 assert(hw_resources.resources[0].type == IO_RANGE);
537 assert(hw_resources.resources[0].res.io_range.size == 8);
538
539 bus->conf_io_addr =
540 (uint32_t) hw_resources.resources[0].res.io_range.address;
541
542 if (pio_enable((void *)(uintptr_t)bus->conf_io_addr, 8,
543 &bus->conf_addr_port)) {
544 printf(NAME ": failed to enable configuration ports.\n");
545 rc = EADDRNOTAVAIL;
546 goto fail;
547 }
548 bus->conf_data_port = (char *) bus->conf_addr_port + 4;
549
550 /* Make the bus device more visible. It has no use yet. */
551 printf(NAME ": adding a 'ctl' function\n");
552
553 ctl = ddf_fun_create(bus->dnode, fun_exposed, "ctl");
554 if (ctl == NULL) {
555 printf(NAME ": error creating control function.\n");
556 rc = ENOMEM;
557 goto fail;
558 }
559
560 rc = ddf_fun_bind(ctl);
561 if (rc != EOK) {
562 printf(NAME ": error binding control function.\n");
563 goto fail;
564 }
565
566 /* Enumerate functions. */
567 printf(NAME ": scanning the bus\n");
568 pci_bus_scan(bus, 0);
569
570 hw_res_clean_resource_list(&hw_resources);
571
572 return EOK;
573
574fail:
575 if (bus != NULL)
576 pci_bus_delete(bus);
577 if (dnode->parent_phone >= 0)
578 async_hangup(dnode->parent_phone);
579 if (got_res)
580 hw_res_clean_resource_list(&hw_resources);
581 if (ctl != NULL)
582 ddf_fun_destroy(ctl);
583
584 return rc;
585}
586
587static void pciintel_init(void)
588{
589 pci_fun_ops.interfaces[HW_RES_DEV_IFACE] = &pciintel_hw_res_ops;
590}
591
592pci_fun_t *pci_fun_new(pci_bus_t *bus)
593{
594 pci_fun_t *fun;
595
596 fun = (pci_fun_t *) calloc(1, sizeof(pci_fun_t));
597 if (fun == NULL)
598 return NULL;
599
600 fun->busptr = bus;
601 return fun;
602}
603
604void pci_fun_init(pci_fun_t *fun, int bus, int dev, int fn)
605{
606 fun->bus = bus;
607 fun->dev = dev;
608 fun->fn = fn;
609}
610
611void pci_fun_delete(pci_fun_t *fun)
612{
613 assert(fun != NULL);
614 hw_res_clean_resource_list(&fun->hw_resources);
615 free(fun);
616}
617
618char *pci_fun_create_name(pci_fun_t *fun)
619{
620 char *name = NULL;
621
622 asprintf(&name, "%02x:%02x.%01x", fun->bus, fun->dev,
623 fun->fn);
624 return name;
625}
626
627bool pci_alloc_resource_list(pci_fun_t *fun)
628{
629 fun->hw_resources.resources =
630 (hw_resource_t *) malloc(PCI_MAX_HW_RES * sizeof(hw_resource_t));
631 return fun->hw_resources.resources != NULL;
632}
633
634void pci_clean_resource_list(pci_fun_t *fun)
635{
636 if (fun->hw_resources.resources != NULL) {
637 free(fun->hw_resources.resources);
638 fun->hw_resources.resources = NULL;
639 }
640}
641
642/** Read the base address registers (BARs) of the function and add the addresses
643 * to its HW resource list.
644 *
645 * @param fun PCI function
646 */
647void pci_read_bars(pci_fun_t *fun)
648{
649 /*
650 * Position of the BAR in the PCI configuration address space of the
651 * device.
652 */
653 int addr = PCI_BASE_ADDR_0;
654
655 while (addr <= PCI_BASE_ADDR_5)
656 addr = pci_read_bar(fun, addr);
657}
658
659size_t pci_bar_mask_to_size(uint32_t mask)
660{
661 return ((mask & 0xfffffff0) ^ 0xffffffff) + 1;
662}
663
664int main(int argc, char *argv[])
665{
666 printf(NAME ": HelenOS pci bus driver (intel method 1).\n");
667 pciintel_init();
668 return ddf_driver_main(&pci_driver);
669}
670
671/**
672 * @}
673 */
Note: See TracBrowser for help on using the repository browser.