source: mainline/uspace/drv/bus/pci/pciintel/pci.c@ 9ef495f

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 9ef495f was f9b2cb4c, checked in by Martin Decky <martin@…>, 10 years ago

unify interface API

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