source: mainline/uspace/drv/bus/pci/pciintel/pci.c@ 1433ecda

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 1433ecda was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 8 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

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