source: mainline/uspace/drv/bus/pci/pciintel/pci.c@ 65f77f4

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 65f77f4 was 65f77f4, checked in by Jakub Jermar <jakub@…>, 12 years ago

Do not add interrupt for devices that do not use an interrupt pin.

  • Property mode set to 100644
File size: 21.0 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 <byteorder.h>
41#include <stdio.h>
42#include <errno.h>
43#include <stdbool.h>
44#include <fibril_synch.h>
45#include <str.h>
46#include <ctype.h>
47#include <macros.h>
48#include <str_error.h>
49
50#include <ddf/driver.h>
51#include <ddf/log.h>
52#include <ipc/dev_iface.h>
53#include <ipc/irc.h>
54#include <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 <pci_dev_iface.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 */
70static pci_fun_t *pci_fun(ddf_fun_t *fnode)
71{
72 return ddf_fun_data_get(fnode);
73}
74
75/** Obtain PCI bus soft-state from DDF device node */
76#if 0
77static pci_bus_t *pci_bus(ddf_dev_t *dnode)
78{
79 return ddf_dev_data_get(dnode);
80}
81#endif
82
83/** Obtain PCI bus soft-state from function soft-state */
84static pci_bus_t *pci_bus_from_fun(pci_fun_t *fun)
85{
86 return fun->busptr;
87}
88
89/** Max is 47, align to something nice. */
90#define ID_MAX_STR_LEN 50
91
92static hw_resource_list_t *pciintel_get_resources(ddf_fun_t *fnode)
93{
94 pci_fun_t *fun = pci_fun(fnode);
95
96 if (fun == NULL)
97 return NULL;
98 return &fun->hw_resources;
99}
100
101static bool pciintel_enable_interrupt(ddf_fun_t *fnode)
102{
103 /* This is an old ugly way */
104 assert(fnode);
105 pci_fun_t *dev_data = pci_fun(fnode);
106
107 sysarg_t apic;
108 sysarg_t i8259;
109
110 async_sess_t *irc_sess = NULL;
111
112 if (((sysinfo_get_value("apic", &apic) == EOK) && (apic))
113 || ((sysinfo_get_value("i8259", &i8259) == EOK) && (i8259))) {
114 irc_sess = service_connect_blocking(EXCHANGE_SERIALIZE,
115 SERVICE_IRC, 0, 0);
116 }
117
118 if (!irc_sess)
119 return false;
120
121 size_t i = 0;
122 hw_resource_list_t *res = &dev_data->hw_resources;
123 for (; i < res->count; i++) {
124 if (res->resources[i].type == INTERRUPT) {
125 const int irq = res->resources[i].res.interrupt.irq;
126
127 async_exch_t *exch = async_exchange_begin(irc_sess);
128 const int rc =
129 async_req_1_0(exch, IRC_ENABLE_INTERRUPT, irq);
130 async_exchange_end(exch);
131
132 if (rc != EOK) {
133 async_hangup(irc_sess);
134 return false;
135 }
136 }
137 }
138
139 async_hangup(irc_sess);
140 return true;
141}
142
143static int pci_config_space_write_32(ddf_fun_t *fun, uint32_t address,
144 uint32_t data)
145{
146 if (address > 252)
147 return EINVAL;
148 pci_conf_write_32(pci_fun(fun), address, data);
149 return EOK;
150}
151
152static int pci_config_space_write_16(
153 ddf_fun_t *fun, uint32_t address, uint16_t data)
154{
155 if (address > 254)
156 return EINVAL;
157 pci_conf_write_16(pci_fun(fun), address, data);
158 return EOK;
159}
160
161static int pci_config_space_write_8(
162 ddf_fun_t *fun, uint32_t address, uint8_t data)
163{
164 if (address > 255)
165 return EINVAL;
166 pci_conf_write_8(pci_fun(fun), address, data);
167 return EOK;
168}
169
170static int pci_config_space_read_32(
171 ddf_fun_t *fun, uint32_t address, uint32_t *data)
172{
173 if (address > 252)
174 return EINVAL;
175 *data = pci_conf_read_32(pci_fun(fun), address);
176 return EOK;
177}
178
179static int pci_config_space_read_16(
180 ddf_fun_t *fun, uint32_t address, uint16_t *data)
181{
182 if (address > 254)
183 return EINVAL;
184 *data = pci_conf_read_16(pci_fun(fun), address);
185 return EOK;
186}
187
188static int pci_config_space_read_8(
189 ddf_fun_t *fun, uint32_t address, uint8_t *data)
190{
191 if (address > 255)
192 return EINVAL;
193 *data = pci_conf_read_8(pci_fun(fun), address);
194 return EOK;
195}
196
197static hw_res_ops_t pciintel_hw_res_ops = {
198 .get_resource_list = &pciintel_get_resources,
199 .enable_interrupt = &pciintel_enable_interrupt,
200};
201
202static pci_dev_iface_t pci_dev_ops = {
203 .config_space_read_8 = &pci_config_space_read_8,
204 .config_space_read_16 = &pci_config_space_read_16,
205 .config_space_read_32 = &pci_config_space_read_32,
206 .config_space_write_8 = &pci_config_space_write_8,
207 .config_space_write_16 = &pci_config_space_write_16,
208 .config_space_write_32 = &pci_config_space_write_32
209};
210
211static ddf_dev_ops_t pci_fun_ops = {
212 .interfaces[HW_RES_DEV_IFACE] = &pciintel_hw_res_ops,
213 .interfaces[PCI_DEV_IFACE] = &pci_dev_ops
214};
215
216static int pci_dev_add(ddf_dev_t *);
217static int pci_fun_online(ddf_fun_t *);
218static int pci_fun_offline(ddf_fun_t *);
219
220/** PCI bus driver standard operations */
221static driver_ops_t pci_ops = {
222 .dev_add = &pci_dev_add,
223 .fun_online = &pci_fun_online,
224 .fun_offline = &pci_fun_offline,
225};
226
227/** PCI bus driver structure */
228static driver_t pci_driver = {
229 .name = NAME,
230 .driver_ops = &pci_ops
231};
232
233static void pci_conf_read(pci_fun_t *fun, int reg, uint8_t *buf, size_t len)
234{
235 const uint32_t conf_addr = CONF_ADDR(fun->bus, fun->dev, fun->fn, reg);
236 pci_bus_t *bus = pci_bus_from_fun(fun);
237 uint32_t val;
238
239 fibril_mutex_lock(&bus->conf_mutex);
240
241 pio_write_32(bus->conf_addr_port, host2uint32_t_le(conf_addr));
242
243 /*
244 * Always read full 32-bits from the PCI conf_data_port register and
245 * get the desired portion of it afterwards. Some architectures do not
246 * support shorter PIO reads offset from this register.
247 */
248 val = uint32_t_le2host(pio_read_32(bus->conf_data_port));
249
250 switch (len) {
251 case 1:
252 *buf = (uint8_t) (val >> ((reg & 3) * 8));
253 break;
254 case 2:
255 *((uint16_t *) buf) = (uint16_t) (val >> ((reg & 3)) * 8);
256 break;
257 case 4:
258 *((uint32_t *) buf) = (uint32_t) val;
259 break;
260 }
261
262 fibril_mutex_unlock(&bus->conf_mutex);
263}
264
265static void pci_conf_write(pci_fun_t *fun, int reg, uint8_t *buf, size_t len)
266{
267 const uint32_t conf_addr = CONF_ADDR(fun->bus, fun->dev, fun->fn, reg);
268 pci_bus_t *bus = pci_bus_from_fun(fun);
269 uint32_t val;
270
271 fibril_mutex_lock(&bus->conf_mutex);
272
273 /*
274 * Prepare to write full 32-bits to the PCI conf_data_port register.
275 * Some architectures do not support shorter PIO writes offset from this
276 * register.
277 */
278
279 if (len < 4) {
280 /*
281 * We have fewer than full 32-bits, so we need to read the
282 * missing bits first.
283 */
284 pio_write_32(bus->conf_addr_port, host2uint32_t_le(conf_addr));
285 val = uint32_t_le2host(pio_read_32(bus->conf_data_port));
286 }
287
288 switch (len) {
289 case 1:
290 val &= ~(0xffU << ((reg & 3) * 8));
291 val |= *buf << ((reg & 3) * 8);
292 break;
293 case 2:
294 val &= ~(0xffffU << ((reg & 3) * 8));
295 val |= *((uint16_t *) buf) << ((reg & 3) * 8);
296 break;
297 case 4:
298 val = *((uint32_t *) buf);
299 break;
300 }
301
302 pio_write_32(bus->conf_addr_port, host2uint32_t_le(conf_addr));
303 pio_write_32(bus->conf_data_port, host2uint32_t_le(val));
304
305 fibril_mutex_unlock(&bus->conf_mutex);
306}
307
308uint8_t pci_conf_read_8(pci_fun_t *fun, int reg)
309{
310 uint8_t res;
311 pci_conf_read(fun, reg, &res, 1);
312 return res;
313}
314
315uint16_t pci_conf_read_16(pci_fun_t *fun, int reg)
316{
317 uint16_t res;
318 pci_conf_read(fun, reg, (uint8_t *) &res, 2);
319 return res;
320}
321
322uint32_t pci_conf_read_32(pci_fun_t *fun, int reg)
323{
324 uint32_t res;
325 pci_conf_read(fun, reg, (uint8_t *) &res, 4);
326 return res;
327}
328
329void pci_conf_write_8(pci_fun_t *fun, int reg, uint8_t val)
330{
331 pci_conf_write(fun, reg, (uint8_t *) &val, 1);
332}
333
334void pci_conf_write_16(pci_fun_t *fun, int reg, uint16_t val)
335{
336 pci_conf_write(fun, reg, (uint8_t *) &val, 2);
337}
338
339void pci_conf_write_32(pci_fun_t *fun, int reg, uint32_t val)
340{
341 pci_conf_write(fun, reg, (uint8_t *) &val, 4);
342}
343
344void pci_fun_create_match_ids(pci_fun_t *fun)
345{
346 int rc;
347 char match_id_str[ID_MAX_STR_LEN];
348
349 /* Vendor ID & Device ID, length(incl \0) 22 */
350 rc = snprintf(match_id_str, ID_MAX_STR_LEN, "pci/ven=%04"
351 PRIx16 "&dev=%04" PRIx16, fun->vendor_id, fun->device_id);
352 if (rc < 0) {
353 ddf_msg(LVL_ERROR, "Failed creating match ID str: %s",
354 str_error(rc));
355 }
356
357 rc = ddf_fun_add_match_id(fun->fnode, match_id_str, 90);
358 if (rc != EOK) {
359 ddf_msg(LVL_ERROR, "Failed adding match ID: %s", str_error(rc));
360 }
361
362 /* Class, subclass, prog IF, revision, length(incl \0) 47 */
363 rc = snprintf(match_id_str, ID_MAX_STR_LEN,
364 "pci/class=%02x&subclass=%02x&progif=%02x&revision=%02x",
365 fun->class_code, fun->subclass_code, fun->prog_if, fun->revision);
366 if (rc < 0) {
367 ddf_msg(LVL_ERROR, "Failed creating match ID str: %s",
368 str_error(rc));
369 }
370
371 rc = ddf_fun_add_match_id(fun->fnode, match_id_str, 70);
372 if (rc != EOK) {
373 ddf_msg(LVL_ERROR, "Failed adding match ID: %s", str_error(rc));
374 }
375
376 /* Class, subclass, prog IF, length(incl \0) 35 */
377 rc = snprintf(match_id_str, ID_MAX_STR_LEN,
378 "pci/class=%02x&subclass=%02x&progif=%02x",
379 fun->class_code, fun->subclass_code, fun->prog_if);
380 if (rc < 0) {
381 ddf_msg(LVL_ERROR, "Failed creating match ID str: %s",
382 str_error(rc));
383 }
384
385 rc = ddf_fun_add_match_id(fun->fnode, match_id_str, 60);
386 if (rc != EOK) {
387 ddf_msg(LVL_ERROR, "Failed adding match ID: %s", str_error(rc));
388 }
389
390 /* Class, subclass, length(incl \0) 25 */
391 rc = snprintf(match_id_str, ID_MAX_STR_LEN,
392 "pci/class=%02x&subclass=%02x",
393 fun->class_code, fun->subclass_code);
394 if (rc < 0) {
395 ddf_msg(LVL_ERROR, "Failed creating match ID str: %s",
396 str_error(rc));
397 }
398
399 rc = ddf_fun_add_match_id(fun->fnode, match_id_str, 50);
400 if (rc != EOK) {
401 ddf_msg(LVL_ERROR, "Failed adding match ID: %s", str_error(rc));
402 }
403
404 /* Class, length(incl \0) 13 */
405 rc = snprintf(match_id_str, ID_MAX_STR_LEN, "pci/class=%02x",
406 fun->class_code);
407 if (rc < 0) {
408 ddf_msg(LVL_ERROR, "Failed creating match ID str: %s",
409 str_error(rc));
410 }
411
412 rc = ddf_fun_add_match_id(fun->fnode, match_id_str, 40);
413 if (rc != EOK) {
414 ddf_msg(LVL_ERROR, "Failed adding match ID: %s", str_error(rc));
415 }
416
417 /* TODO add subsys ids, but those exist only in header type 0 */
418}
419
420void pci_add_range(pci_fun_t *fun, uint64_t range_addr, size_t range_size,
421 bool io)
422{
423 hw_resource_list_t *hw_res_list = &fun->hw_resources;
424 hw_resource_t *hw_resources = hw_res_list->resources;
425 size_t count = hw_res_list->count;
426
427 assert(hw_resources != NULL);
428 assert(count < PCI_MAX_HW_RES);
429
430 if (io) {
431 hw_resources[count].type = IO_RANGE;
432 hw_resources[count].res.io_range.address = range_addr;
433 hw_resources[count].res.io_range.size = range_size;
434 hw_resources[count].res.io_range.endianness = LITTLE_ENDIAN;
435 } else {
436 hw_resources[count].type = MEM_RANGE;
437 hw_resources[count].res.mem_range.address = range_addr;
438 hw_resources[count].res.mem_range.size = range_size;
439 hw_resources[count].res.mem_range.endianness = LITTLE_ENDIAN;
440 }
441
442 hw_res_list->count++;
443}
444
445/** Read the base address register (BAR) of the device and if it contains valid
446 * address add it to the devices hw resource list.
447 *
448 * @param fun PCI function
449 * @param addr The address of the BAR in the PCI configuration address space of
450 * the device
451 * @return The addr the address of the BAR which should be read next
452 */
453int pci_read_bar(pci_fun_t *fun, int addr)
454{
455 /* Value of the BAR */
456 uint32_t val;
457 uint32_t bar;
458 uint32_t mask;
459
460 /* IO space address */
461 bool io;
462 /* 64-bit wide address */
463 bool addrw64;
464
465 /* Size of the io or memory range specified by the BAR */
466 size_t range_size;
467 /* Beginning of the io or memory range specified by the BAR */
468 uint64_t range_addr;
469
470 /* Get the value of the BAR. */
471 val = pci_conf_read_32(fun, addr);
472
473#define IO_MASK (~0x3)
474#define MEM_MASK (~0xf)
475
476 io = (bool) (val & 1);
477 if (io) {
478 addrw64 = false;
479 mask = IO_MASK;
480 } else {
481 mask = MEM_MASK;
482 switch ((val >> 1) & 3) {
483 case 0:
484 addrw64 = false;
485 break;
486 case 2:
487 addrw64 = true;
488 break;
489 default:
490 /* reserved, go to the next BAR */
491 return addr + 4;
492 }
493 }
494
495 /* Get the address mask. */
496 pci_conf_write_32(fun, addr, 0xffffffff);
497 bar = pci_conf_read_32(fun, addr);
498
499 /*
500 * Unimplemented BARs read back as all 0's.
501 */
502 if (!bar)
503 return addr + (addrw64 ? 8 : 4);
504
505 mask &= bar;
506
507 /* Restore the original value. */
508 pci_conf_write_32(fun, addr, val);
509 val = pci_conf_read_32(fun, addr);
510
511 range_size = pci_bar_mask_to_size(mask);
512
513 if (addrw64) {
514 range_addr = ((uint64_t)pci_conf_read_32(fun, addr + 4) << 32) |
515 (val & 0xfffffff0);
516 } else {
517 range_addr = (val & 0xfffffff0);
518 }
519
520 if (range_addr != 0) {
521 ddf_msg(LVL_DEBUG, "Function %s : address = %" PRIx64
522 ", size = %x", ddf_fun_get_name(fun->fnode), range_addr,
523 (unsigned int) range_size);
524 }
525
526 pci_add_range(fun, range_addr, range_size, io);
527
528 if (addrw64)
529 return addr + 8;
530
531 return addr + 4;
532}
533
534void pci_add_interrupt(pci_fun_t *fun, int irq)
535{
536 hw_resource_list_t *hw_res_list = &fun->hw_resources;
537 hw_resource_t *hw_resources = hw_res_list->resources;
538 size_t count = hw_res_list->count;
539
540 assert(NULL != hw_resources);
541 assert(count < PCI_MAX_HW_RES);
542
543 hw_resources[count].type = INTERRUPT;
544 hw_resources[count].res.interrupt.irq = irq;
545
546 hw_res_list->count++;
547
548 ddf_msg(LVL_NOTE, "Function %s uses irq %x.", ddf_fun_get_name(fun->fnode), irq);
549}
550
551void pci_read_interrupt(pci_fun_t *fun)
552{
553 uint8_t irq = pci_conf_read_8(fun, PCI_BRIDGE_INT_LINE);
554 uint8_t pin = pci_conf_read_8(fun, PCI_BRIDGE_INT_PIN);
555
556 if (pin != 0 && irq != 0xff)
557 pci_add_interrupt(fun, irq);
558}
559
560/** Enumerate (recursively) and register the devices connected to a pci bus.
561 *
562 * @param bus Host-to-PCI bridge
563 * @param bus_num Bus number
564 */
565void pci_bus_scan(pci_bus_t *bus, int bus_num)
566{
567 pci_fun_t *fun;
568 int rc;
569
570 int child_bus = 0;
571 int dnum, fnum;
572 bool multi;
573 uint8_t header_type;
574
575 for (dnum = 0; dnum < 32; dnum++) {
576 multi = true;
577 for (fnum = 0; multi && fnum < 8; fnum++) {
578 fun = pci_fun_new(bus);
579
580 pci_fun_init(fun, bus_num, dnum, fnum);
581 if (fun->vendor_id == 0xffff) {
582 pci_fun_delete(fun);
583 /*
584 * The device is not present, go on scanning the
585 * bus.
586 */
587 if (fnum == 0)
588 break;
589 else
590 continue;
591 }
592
593 header_type = pci_conf_read_8(fun, PCI_HEADER_TYPE);
594 if (fnum == 0) {
595 /* Is the device multifunction? */
596 multi = header_type >> 7;
597 }
598 /* Clear the multifunction bit. */
599 header_type = header_type & 0x7F;
600
601 char *fun_name = pci_fun_create_name(fun);
602 if (fun_name == NULL) {
603 ddf_msg(LVL_ERROR, "Out of memory.");
604 pci_fun_delete(fun);
605 return;
606 }
607
608 rc = ddf_fun_set_name(fun->fnode, fun_name);
609 free(fun_name);
610 if (rc != EOK) {
611 ddf_msg(LVL_ERROR, "Failed setting function name.");
612 pci_fun_delete(fun);
613 return;
614 }
615
616 pci_alloc_resource_list(fun);
617 pci_read_bars(fun);
618 pci_read_interrupt(fun);
619
620 ddf_fun_set_ops(fun->fnode, &pci_fun_ops);
621
622 ddf_msg(LVL_DEBUG, "Adding new function %s.",
623 ddf_fun_get_name(fun->fnode));
624
625 pci_fun_create_match_ids(fun);
626
627 if (ddf_fun_bind(fun->fnode) != EOK) {
628 pci_clean_resource_list(fun);
629 pci_fun_delete(fun);
630 continue;
631 }
632
633 if (header_type == PCI_HEADER_TYPE_BRIDGE ||
634 header_type == PCI_HEADER_TYPE_CARDBUS) {
635 child_bus = pci_conf_read_8(fun,
636 PCI_BRIDGE_SEC_BUS_NUM);
637 ddf_msg(LVL_DEBUG, "Device is pci-to-pci "
638 "bridge, secondary bus number = %d.",
639 bus_num);
640 if (child_bus > bus_num)
641 pci_bus_scan(bus, child_bus);
642 }
643 }
644 }
645}
646
647static int pci_dev_add(ddf_dev_t *dnode)
648{
649 pci_bus_t *bus = NULL;
650 ddf_fun_t *ctl = NULL;
651 bool got_res = false;
652 async_sess_t *sess;
653 int rc;
654
655 ddf_msg(LVL_DEBUG, "pci_dev_add");
656
657 bus = ddf_dev_data_alloc(dnode, sizeof(pci_bus_t));
658 if (bus == NULL) {
659 ddf_msg(LVL_ERROR, "pci_dev_add allocation failed.");
660 rc = ENOMEM;
661 goto fail;
662 }
663 fibril_mutex_initialize(&bus->conf_mutex);
664
665 bus->dnode = dnode;
666
667 sess = ddf_dev_parent_sess_create(dnode, EXCHANGE_SERIALIZE);
668 if (sess == NULL) {
669 ddf_msg(LVL_ERROR, "pci_dev_add failed to connect to the "
670 "parent driver.");
671 rc = ENOENT;
672 goto fail;
673 }
674
675 hw_resource_list_t hw_resources;
676
677 rc = hw_res_get_resource_list(sess, &hw_resources);
678 if (rc != EOK) {
679 ddf_msg(LVL_ERROR, "pci_dev_add failed to get hw resources "
680 "for the device.");
681 goto fail;
682 }
683 got_res = true;
684
685
686 assert(hw_resources.count > 1);
687 assert(hw_resources.resources[0].type == IO_RANGE);
688 assert(hw_resources.resources[0].res.io_range.size >= 4);
689
690 assert(hw_resources.resources[1].type == IO_RANGE);
691 assert(hw_resources.resources[1].res.io_range.size >= 4);
692
693 ddf_msg(LVL_DEBUG, "conf_addr = %" PRIx64 ".",
694 hw_resources.resources[0].res.io_range.address);
695 ddf_msg(LVL_DEBUG, "data_addr = %" PRIx64 ".",
696 hw_resources.resources[1].res.io_range.address);
697
698 bus->conf_io_addr =
699 (uint32_t) hw_resources.resources[0].res.io_range.address;
700 bus->conf_io_data =
701 (uint32_t) hw_resources.resources[1].res.io_range.address;
702
703 if (pio_enable((void *)(uintptr_t)bus->conf_io_addr, 4,
704 &bus->conf_addr_port)) {
705 ddf_msg(LVL_ERROR, "Failed to enable configuration ports.");
706 rc = EADDRNOTAVAIL;
707 goto fail;
708 }
709 if (pio_enable((void *)(uintptr_t)bus->conf_io_data, 4,
710 &bus->conf_data_port)) {
711 ddf_msg(LVL_ERROR, "Failed to enable configuration ports.");
712 rc = EADDRNOTAVAIL;
713 goto fail;
714 }
715
716 /* Make the bus device more visible. It has no use yet. */
717 ddf_msg(LVL_DEBUG, "Adding a 'ctl' function");
718
719 ctl = ddf_fun_create(bus->dnode, fun_exposed, "ctl");
720 if (ctl == NULL) {
721 ddf_msg(LVL_ERROR, "Failed creating control function.");
722 rc = ENOMEM;
723 goto fail;
724 }
725
726 rc = ddf_fun_bind(ctl);
727 if (rc != EOK) {
728 ddf_msg(LVL_ERROR, "Failed binding control function.");
729 goto fail;
730 }
731
732 /* Enumerate functions. */
733 ddf_msg(LVL_DEBUG, "Scanning the bus");
734 pci_bus_scan(bus, 0);
735
736 hw_res_clean_resource_list(&hw_resources);
737
738 return EOK;
739
740fail:
741 if (got_res)
742 hw_res_clean_resource_list(&hw_resources);
743
744 if (ctl != NULL)
745 ddf_fun_destroy(ctl);
746
747 return rc;
748}
749
750static int pci_fun_online(ddf_fun_t *fun)
751{
752 ddf_msg(LVL_DEBUG, "pci_fun_online()");
753 return ddf_fun_online(fun);
754}
755
756static int pci_fun_offline(ddf_fun_t *fun)
757{
758 ddf_msg(LVL_DEBUG, "pci_fun_offline()");
759 return ddf_fun_offline(fun);
760}
761
762static void pciintel_init(void)
763{
764 ddf_log_init(NAME);
765 pci_fun_ops.interfaces[HW_RES_DEV_IFACE] = &pciintel_hw_res_ops;
766 pci_fun_ops.interfaces[PCI_DEV_IFACE] = &pci_dev_ops;
767}
768
769pci_fun_t *pci_fun_new(pci_bus_t *bus)
770{
771 pci_fun_t *fun;
772 ddf_fun_t *fnode;
773
774 fnode = ddf_fun_create(bus->dnode, fun_inner, NULL);
775 if (fnode == NULL)
776 return NULL;
777
778 fun = ddf_fun_data_alloc(fnode, sizeof(pci_fun_t));
779 if (fun == NULL)
780 return NULL;
781
782 fun->busptr = bus;
783 fun->fnode = fnode;
784 return fun;
785}
786
787void pci_fun_init(pci_fun_t *fun, int bus, int dev, int fn)
788{
789 fun->bus = bus;
790 fun->dev = dev;
791 fun->fn = fn;
792 fun->vendor_id = pci_conf_read_16(fun, PCI_VENDOR_ID);
793 fun->device_id = pci_conf_read_16(fun, PCI_DEVICE_ID);
794
795 /* Explicitly enable PCI bus mastering */
796 fun->command = pci_conf_read_16(fun, PCI_COMMAND) |
797 PCI_COMMAND_MASTER;
798 pci_conf_write_16(fun, PCI_COMMAND, fun->command);
799
800 fun->class_code = pci_conf_read_8(fun, PCI_BASE_CLASS);
801 fun->subclass_code = pci_conf_read_8(fun, PCI_SUB_CLASS);
802 fun->prog_if = pci_conf_read_8(fun, PCI_PROG_IF);
803 fun->revision = pci_conf_read_8(fun, PCI_REVISION_ID);
804}
805
806void pci_fun_delete(pci_fun_t *fun)
807{
808 hw_res_clean_resource_list(&fun->hw_resources);
809 if (fun->fnode != NULL)
810 ddf_fun_destroy(fun->fnode);
811}
812
813char *pci_fun_create_name(pci_fun_t *fun)
814{
815 char *name = NULL;
816
817 asprintf(&name, "%02x:%02x.%01x", fun->bus, fun->dev,
818 fun->fn);
819 return name;
820}
821
822bool pci_alloc_resource_list(pci_fun_t *fun)
823{
824 fun->hw_resources.resources = fun->resources;
825 return true;
826}
827
828void pci_clean_resource_list(pci_fun_t *fun)
829{
830 fun->hw_resources.resources = NULL;
831}
832
833/** Read the base address registers (BARs) of the function and add the addresses
834 * to its HW resource list.
835 *
836 * @param fun PCI function
837 */
838void pci_read_bars(pci_fun_t *fun)
839{
840 /*
841 * Position of the BAR in the PCI configuration address space of the
842 * device.
843 */
844 int addr = PCI_BASE_ADDR_0;
845
846 while (addr <= PCI_BASE_ADDR_5)
847 addr = pci_read_bar(fun, addr);
848}
849
850size_t pci_bar_mask_to_size(uint32_t mask)
851{
852 size_t size = mask & ~(mask - 1);
853 return size;
854}
855
856int main(int argc, char *argv[])
857{
858 printf(NAME ": HelenOS PCI bus driver (Intel method 1).\n");
859 pciintel_init();
860 return ddf_driver_main(&pci_driver);
861}
862
863/**
864 * @}
865 */
Note: See TracBrowser for help on using the repository browser.