source: mainline/uspace/drv/pciintel/pci.c@ 969585f

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

Merge mainline changes

  • Property mode set to 100644
File size: 17.4 KB
RevLine 
[8c06905]1/*
2 * Copyright (c) 2010 Lenka Trochtova
[68414f4a]3 * Copyright (c) 2011 Jiri Svoboda
[8c06905]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>
[c47e1a8]44#include <str.h>
[8c06905]45#include <ctype.h>
46#include <macros.h>
[cd0684d]47#include <str_error.h>
[8c06905]48
[af6b5157]49#include <ddf/driver.h>
[fc51296]50#include <ddf/log.h>
[8c06905]51#include <devman.h>
52#include <ipc/devman.h>
53#include <ipc/dev_iface.h>
[fb78ae72]54#include <ipc/irc.h>
55#include <ipc/ns.h>
56#include <ipc/services.h>
57#include <sysinfo.h>
[41b56084]58#include <ops/hw_res.h>
[8c06905]59#include <device/hw_res.h>
60#include <ddi.h>
[5e598e0]61#include <libarch/ddi.h>
[99e6bfb]62#include <pci_dev_iface.h>
[5e598e0]63
64#include "pci.h"
[8c06905]65
66#define NAME "pciintel"
67
[663f41c4]68#define CONF_ADDR(bus, dev, fn, reg) \
69 ((1 << 31) | (bus << 16) | (dev << 11) | (fn << 8) | (reg & ~3))
[5e598e0]70
[68414f4a]71/** Obtain PCI function soft-state from DDF function node */
72#define PCI_FUN(fnode) ((pci_fun_t *) (fnode)->driver_data)
73
74/** Obtain PCI bus soft-state from DDF device node */
75#define PCI_BUS(dnode) ((pci_bus_t *) (dnode)->driver_data)
76
77/** Obtain PCI bus soft-state from function soft-state */
[97a62fe]78#define PCI_BUS_FROM_FUN(fun) ((fun)->busptr)
[68414f4a]79
[83a2f43]80static hw_resource_list_t *pciintel_get_resources(ddf_fun_t *fnode)
[3843ecb]81{
[68414f4a]82 pci_fun_t *fun = PCI_FUN(fnode);
[663f41c4]83
[68414f4a]84 if (fun == NULL)
[3843ecb]85 return NULL;
[68414f4a]86 return &fun->hw_resources;
[3843ecb]87}
88
[83a2f43]89static bool pciintel_enable_interrupt(ddf_fun_t *fnode)
[3843ecb]90{
[fb78ae72]91 /* This is an old ugly way, copied from ne2000 driver */
[eb1a2f4]92 assert(fnode);
93 pci_fun_t *dev_data = (pci_fun_t *) fnode->driver_data;
[fb78ae72]94
[91579d5]95 sysarg_t apic;
96 sysarg_t i8259;
[dc75234]97
[51e5608]98 int irc_phone = ENOTSUP;
[fb78ae72]99
[51e5608]100 if (((sysinfo_get_value("apic", &apic) == EOK) && (apic))
101 || ((sysinfo_get_value("i8259", &i8259) == EOK) && (i8259))) {
102 irc_phone = service_connect_blocking(SERVICE_IRC, 0, 0);
[fb78ae72]103 }
104
[91579d5]105 if (irc_phone < 0) {
[fb78ae72]106 return false;
[91579d5]107 }
[fb78ae72]108
109 size_t i;
[91579d5]110 for (i = 0; i < dev_data->hw_resources.count; i++) {
[fb78ae72]111 if (dev_data->hw_resources.resources[i].type == INTERRUPT) {
112 int irq = dev_data->hw_resources.resources[i].res.interrupt.irq;
[dc75234]113 int rc = async_req_1_0(irc_phone, IRC_ENABLE_INTERRUPT, irq);
114 if (rc != EOK) {
115 async_hangup(irc_phone);
116 return false;
117 }
[fb78ae72]118 }
119 }
120
121 async_hangup(irc_phone);
122 return true;
[3843ecb]123}
124
[40a5d40]125static int pci_config_space_write_32(
126 ddf_fun_t *fun, uint32_t address, uint32_t data)
127{
128 if (address > 252)
129 return EINVAL;
130 pci_conf_write_32(PCI_FUN(fun), address, data);
131 return EOK;
132}
133
134static int pci_config_space_write_16(
135 ddf_fun_t *fun, uint32_t address, uint16_t data)
[99e6bfb]136{
137 if (address > 254)
138 return EINVAL;
139 pci_conf_write_16(PCI_FUN(fun), address, data);
140 return EOK;
141}
142
[40a5d40]143static int pci_config_space_write_8(
144 ddf_fun_t *fun, uint32_t address, uint8_t data)
145{
146 if (address > 255)
147 return EINVAL;
148 pci_conf_write_8(PCI_FUN(fun), address, data);
149 return EOK;
150}
151
152static int pci_config_space_read_32(
153 ddf_fun_t *fun, uint32_t address, uint32_t *data)
154{
155 if (address > 252)
156 return EINVAL;
157 *data = pci_conf_read_32(PCI_FUN(fun), address);
158 return EOK;
159}
160
161static int pci_config_space_read_16(
162 ddf_fun_t *fun, uint32_t address, uint16_t *data)
163{
164 if (address > 254)
165 return EINVAL;
166 *data = pci_conf_read_16(PCI_FUN(fun), address);
167 return EOK;
168}
169
170static int pci_config_space_read_8(
171 ddf_fun_t *fun, uint32_t address, uint8_t *data)
172{
173 if (address > 255)
174 return EINVAL;
175 *data = pci_conf_read_8(PCI_FUN(fun), address);
176 return EOK;
177}
[99e6bfb]178
[68414f4a]179static hw_res_ops_t pciintel_hw_res_ops = {
180 &pciintel_get_resources,
181 &pciintel_enable_interrupt
[3843ecb]182};
183
[99e6bfb]184static pci_dev_iface_t pci_dev_ops = {
[40a5d40]185 .config_space_read_8 = &pci_config_space_read_8,
186 .config_space_read_16 = &pci_config_space_read_16,
187 .config_space_read_32 = &pci_config_space_read_32,
188 .config_space_write_8 = &pci_config_space_write_8,
[99e6bfb]189 .config_space_write_16 = &pci_config_space_write_16,
[40a5d40]190 .config_space_write_32 = &pci_config_space_write_32
[99e6bfb]191};
192
193static ddf_dev_ops_t pci_fun_ops = {
194 .interfaces[HW_RES_DEV_IFACE] = &pciintel_hw_res_ops,
195 .interfaces[PCI_DEV_IFACE] = &pci_dev_ops
196};
[3843ecb]197
[83a2f43]198static int pci_add_device(ddf_dev_t *);
[3843ecb]199
[68414f4a]200/** PCI bus driver standard operations */
[8c06905]201static driver_ops_t pci_ops = {
202 .add_device = &pci_add_device
203};
204
[68414f4a]205/** PCI bus driver structure */
[8c06905]206static driver_t pci_driver = {
207 .name = NAME,
208 .driver_ops = &pci_ops
209};
210
[68414f4a]211static pci_bus_t *pci_bus_new(void)
[5e598e0]212{
[68414f4a]213 pci_bus_t *bus;
[663f41c4]214
[bab6388]215 bus = (pci_bus_t *) calloc(1, sizeof(pci_bus_t));
216 if (bus == NULL)
217 return NULL;
218
219 fibril_mutex_initialize(&bus->conf_mutex);
[68414f4a]220 return bus;
[5e598e0]221}
222
[68414f4a]223static void pci_bus_delete(pci_bus_t *bus)
[5e598e0]224{
[bab6388]225 assert(bus != NULL);
[68414f4a]226 free(bus);
[5e598e0]227}
228
[68414f4a]229static void pci_conf_read(pci_fun_t *fun, int reg, uint8_t *buf, size_t len)
[5e598e0]230{
[68414f4a]231 pci_bus_t *bus = PCI_BUS_FROM_FUN(fun);
[5e598e0]232
[68414f4a]233 fibril_mutex_lock(&bus->conf_mutex);
[5e598e0]234
[663f41c4]235 uint32_t conf_addr;
[68414f4a]236 conf_addr = CONF_ADDR(fun->bus, fun->dev, fun->fn, reg);
237 void *addr = bus->conf_data_port + (reg & 3);
[5e598e0]238
[68414f4a]239 pio_write_32(bus->conf_addr_port, conf_addr);
[5e598e0]240
241 switch (len) {
[663f41c4]242 case 1:
243 buf[0] = pio_read_8(addr);
244 break;
245 case 2:
246 ((uint16_t *) buf)[0] = pio_read_16(addr);
247 break;
248 case 4:
249 ((uint32_t *) buf)[0] = pio_read_32(addr);
250 break;
[5e598e0]251 }
252
[68414f4a]253 fibril_mutex_unlock(&bus->conf_mutex);
[5e598e0]254}
255
[68414f4a]256static void pci_conf_write(pci_fun_t *fun, int reg, uint8_t *buf, size_t len)
[d1fc8f0]257{
[68414f4a]258 pci_bus_t *bus = PCI_BUS_FROM_FUN(fun);
[d1fc8f0]259
[68414f4a]260 fibril_mutex_lock(&bus->conf_mutex);
[d1fc8f0]261
[663f41c4]262 uint32_t conf_addr;
[68414f4a]263 conf_addr = CONF_ADDR(fun->bus, fun->dev, fun->fn, reg);
264 void *addr = bus->conf_data_port + (reg & 3);
[d1fc8f0]265
[68414f4a]266 pio_write_32(bus->conf_addr_port, conf_addr);
[d1fc8f0]267
268 switch (len) {
[663f41c4]269 case 1:
270 pio_write_8(addr, buf[0]);
271 break;
272 case 2:
273 pio_write_16(addr, ((uint16_t *) buf)[0]);
274 break;
275 case 4:
276 pio_write_32(addr, ((uint32_t *) buf)[0]);
277 break;
[d1fc8f0]278 }
279
[68414f4a]280 fibril_mutex_unlock(&bus->conf_mutex);
[d1fc8f0]281}
282
[68414f4a]283uint8_t pci_conf_read_8(pci_fun_t *fun, int reg)
[5e598e0]284{
285 uint8_t res;
[8b1e15ac]286 pci_conf_read(fun, reg, &res, 1);
[5e598e0]287 return res;
288}
289
[68414f4a]290uint16_t pci_conf_read_16(pci_fun_t *fun, int reg)
[5e598e0]291{
292 uint16_t res;
[8b1e15ac]293 pci_conf_read(fun, reg, (uint8_t *) &res, 2);
[5e598e0]294 return res;
295}
296
[68414f4a]297uint32_t pci_conf_read_32(pci_fun_t *fun, int reg)
[5e598e0]298{
299 uint32_t res;
[8b1e15ac]300 pci_conf_read(fun, reg, (uint8_t *) &res, 4);
[663f41c4]301 return res;
[5e598e0]302}
303
[68414f4a]304void pci_conf_write_8(pci_fun_t *fun, int reg, uint8_t val)
[d1fc8f0]305{
[8b1e15ac]306 pci_conf_write(fun, reg, (uint8_t *) &val, 1);
[d1fc8f0]307}
308
[68414f4a]309void pci_conf_write_16(pci_fun_t *fun, int reg, uint16_t val)
[d1fc8f0]310{
[8b1e15ac]311 pci_conf_write(fun, reg, (uint8_t *) &val, 2);
[d1fc8f0]312}
313
[68414f4a]314void pci_conf_write_32(pci_fun_t *fun, int reg, uint32_t val)
[d1fc8f0]315{
[8b1e15ac]316 pci_conf_write(fun, reg, (uint8_t *) &val, 4);
[d1fc8f0]317}
318
[68414f4a]319void pci_fun_create_match_ids(pci_fun_t *fun)
[89ce401a]320{
[663f41c4]321 char *match_id_str;
[cd0684d]322 int rc;
[663f41c4]323
[cd0684d]324 asprintf(&match_id_str, "pci/ven=%04x&dev=%04x",
325 fun->vendor_id, fun->device_id);
326
327 if (match_id_str == NULL) {
[ebcb05a]328 ddf_msg(LVL_ERROR, "Out of memory creating match ID.");
[cd0684d]329 return;
[8304889]330 }
331
[cd0684d]332 rc = ddf_fun_add_match_id(fun->fnode, match_id_str, 90);
333 if (rc != EOK) {
[ebcb05a]334 ddf_msg(LVL_ERROR, "Failed adding match ID: %s",
[cd0684d]335 str_error(rc));
[8304889]336 }
[bab6388]337
[663f41c4]338 /* TODO add more ids (with subsys ids, using class id etc.) */
[89ce401a]339}
340
[68414f4a]341void pci_add_range(pci_fun_t *fun, uint64_t range_addr, size_t range_size,
342 bool io)
[d1fc8f0]343{
[68414f4a]344 hw_resource_list_t *hw_res_list = &fun->hw_resources;
[3a5909f]345 hw_resource_t *hw_resources = hw_res_list->resources;
[663f41c4]346 size_t count = hw_res_list->count;
[3a5909f]347
[8304889]348 assert(hw_resources != NULL);
[3a5909f]349 assert(count < PCI_MAX_HW_RES);
350
351 if (io) {
352 hw_resources[count].type = IO_RANGE;
353 hw_resources[count].res.io_range.address = range_addr;
[663f41c4]354 hw_resources[count].res.io_range.size = range_size;
355 hw_resources[count].res.io_range.endianness = LITTLE_ENDIAN;
[3a5909f]356 } else {
357 hw_resources[count].type = MEM_RANGE;
358 hw_resources[count].res.mem_range.address = range_addr;
[663f41c4]359 hw_resources[count].res.mem_range.size = range_size;
[3a5909f]360 hw_resources[count].res.mem_range.endianness = LITTLE_ENDIAN;
361 }
362
[663f41c4]363 hw_res_list->count++;
[d1fc8f0]364}
365
[663f41c4]366/** Read the base address register (BAR) of the device and if it contains valid
367 * address add it to the devices hw resource list.
368 *
[68414f4a]369 * @param fun PCI function
[663f41c4]370 * @param addr The address of the BAR in the PCI configuration address space of
[68414f4a]371 * the device
372 * @return The addr the address of the BAR which should be read next
[d1fc8f0]373 */
[68414f4a]374int pci_read_bar(pci_fun_t *fun, int addr)
[bab6388]375{
[663f41c4]376 /* Value of the BAR */
[d1fc8f0]377 uint32_t val, mask;
[663f41c4]378 /* IO space address */
[d1fc8f0]379 bool io;
[663f41c4]380 /* 64-bit wide address */
[d93aafed]381 bool addrw64;
[d1fc8f0]382
[663f41c4]383 /* Size of the io or memory range specified by the BAR */
[d1fc8f0]384 size_t range_size;
[663f41c4]385 /* Beginning of the io or memory range specified by the BAR */
[d1fc8f0]386 uint64_t range_addr;
387
[663f41c4]388 /* Get the value of the BAR. */
[8b1e15ac]389 val = pci_conf_read_32(fun, addr);
[ad6857c]390
391#define IO_MASK (~0x3)
392#define MEM_MASK (~0xf)
[d1fc8f0]393
[663f41c4]394 io = (bool) (val & 1);
[d1fc8f0]395 if (io) {
[d93aafed]396 addrw64 = false;
[ad6857c]397 mask = IO_MASK;
[d1fc8f0]398 } else {
[ad6857c]399 mask = MEM_MASK;
[d1fc8f0]400 switch ((val >> 1) & 3) {
401 case 0:
[d93aafed]402 addrw64 = false;
[d1fc8f0]403 break;
404 case 2:
[d93aafed]405 addrw64 = true;
[d1fc8f0]406 break;
407 default:
[663f41c4]408 /* reserved, go to the next BAR */
409 return addr + 4;
[d1fc8f0]410 }
411 }
412
[663f41c4]413 /* Get the address mask. */
[8b1e15ac]414 pci_conf_write_32(fun, addr, 0xffffffff);
[ad6857c]415 mask &= pci_conf_read_32(fun, addr);
[d1fc8f0]416
[663f41c4]417 /* Restore the original value. */
[8b1e15ac]418 pci_conf_write_32(fun, addr, val);
419 val = pci_conf_read_32(fun, addr);
[d1fc8f0]420
[3a5909f]421 range_size = pci_bar_mask_to_size(mask);
[d1fc8f0]422
[d93aafed]423 if (addrw64) {
[8b1e15ac]424 range_addr = ((uint64_t)pci_conf_read_32(fun, addr + 4) << 32) |
[663f41c4]425 (val & 0xfffffff0);
[d1fc8f0]426 } else {
427 range_addr = (val & 0xfffffff0);
[663f41c4]428 }
429
[d93aafed]430 if (range_addr != 0) {
[fc51296]431 ddf_msg(LVL_DEBUG, "Function %s : address = %" PRIx64
[ebcb05a]432 ", size = %x", fun->fnode->name, range_addr,
[fc51296]433 (unsigned int) range_size);
[d1fc8f0]434 }
435
[8b1e15ac]436 pci_add_range(fun, range_addr, range_size, io);
[d1fc8f0]437
[d93aafed]438 if (addrw64)
[d1fc8f0]439 return addr + 8;
[663f41c4]440
441 return addr + 4;
[d1fc8f0]442}
443
[68414f4a]444void pci_add_interrupt(pci_fun_t *fun, int irq)
[d1fc8f0]445{
[68414f4a]446 hw_resource_list_t *hw_res_list = &fun->hw_resources;
[663f41c4]447 hw_resource_t *hw_resources = hw_res_list->resources;
448 size_t count = hw_res_list->count;
[d1fc8f0]449
[3a5909f]450 assert(NULL != hw_resources);
451 assert(count < PCI_MAX_HW_RES);
452
453 hw_resources[count].type = INTERRUPT;
454 hw_resources[count].res.interrupt.irq = irq;
455
[663f41c4]456 hw_res_list->count++;
[3a5909f]457
[ebcb05a]458 ddf_msg(LVL_NOTE, "Function %s uses irq %x.", fun->fnode->name, irq);
[3a5909f]459}
460
[68414f4a]461void pci_read_interrupt(pci_fun_t *fun)
[3a5909f]462{
[8b1e15ac]463 uint8_t irq = pci_conf_read_8(fun, PCI_BRIDGE_INT_LINE);
[8304889]464 if (irq != 0xff)
[8b1e15ac]465 pci_add_interrupt(fun, irq);
[d1fc8f0]466}
467
468/** Enumerate (recursively) and register the devices connected to a pci bus.
[663f41c4]469 *
[68414f4a]470 * @param bus Host-to-PCI bridge
471 * @param bus_num Bus number
[d1fc8f0]472 */
[68414f4a]473void pci_bus_scan(pci_bus_t *bus, int bus_num)
[5e598e0]474{
[83a2f43]475 ddf_fun_t *fnode;
[97a62fe]476 pci_fun_t *fun;
[5e598e0]477
478 int child_bus = 0;
479 int dnum, fnum;
480 bool multi;
[8b1e15ac]481 uint8_t header_type;
[bab6388]482
[97a62fe]483 fun = pci_fun_new(bus);
[5e598e0]484
485 for (dnum = 0; dnum < 32; dnum++) {
486 multi = true;
487 for (fnum = 0; multi && fnum < 8; fnum++) {
[68414f4a]488 pci_fun_init(fun, bus_num, dnum, fnum);
489 fun->vendor_id = pci_conf_read_16(fun,
[663f41c4]490 PCI_VENDOR_ID);
[68414f4a]491 fun->device_id = pci_conf_read_16(fun,
[663f41c4]492 PCI_DEVICE_ID);
[68414f4a]493 if (fun->vendor_id == 0xffff) {
[663f41c4]494 /*
495 * The device is not present, go on scanning the
496 * bus.
497 */
498 if (fnum == 0)
[5e598e0]499 break;
[663f41c4]500 else
501 continue;
[5e598e0]502 }
[663f41c4]503
[8b1e15ac]504 header_type = pci_conf_read_8(fun, PCI_HEADER_TYPE);
[5e598e0]505 if (fnum == 0) {
[663f41c4]506 /* Is the device multifunction? */
507 multi = header_type >> 7;
[5e598e0]508 }
[663f41c4]509 /* Clear the multifunction bit. */
510 header_type = header_type & 0x7F;
[5e598e0]511
[97a62fe]512 char *fun_name = pci_fun_create_name(fun);
513 if (fun_name == NULL) {
[ebcb05a]514 ddf_msg(LVL_ERROR, "Out of memory.");
[97a62fe]515 return;
516 }
517
518 fnode = ddf_fun_create(bus->dnode, fun_inner, fun_name);
519 if (fnode == NULL) {
[ebcb05a]520 ddf_msg(LVL_ERROR, "Failed creating function.");
[97a62fe]521 return;
522 }
[3a5909f]523
[97a62fe]524 free(fun_name);
525 fun->fnode = fnode;
[3a5909f]526
[8b1e15ac]527 pci_alloc_resource_list(fun);
528 pci_read_bars(fun);
529 pci_read_interrupt(fun);
[89ce401a]530
[68414f4a]531 fnode->ops = &pci_fun_ops;
[97a62fe]532 fnode->driver_data = fun;
[89ce401a]533
[ebcb05a]534 ddf_msg(LVL_DEBUG, "Adding new function %s.",
[68414f4a]535 fnode->name);
[89ce401a]536
[68414f4a]537 pci_fun_create_match_ids(fun);
[89ce401a]538
[97a62fe]539 if (ddf_fun_bind(fnode) != EOK) {
[8b1e15ac]540 pci_clean_resource_list(fun);
[68414f4a]541 clean_match_ids(&fnode->match_ids);
542 free((char *) fnode->name);
543 fnode->name = NULL;
[89ce401a]544 continue;
545 }
[5e598e0]546
[663f41c4]547 if (header_type == PCI_HEADER_TYPE_BRIDGE ||
[8304889]548 header_type == PCI_HEADER_TYPE_CARDBUS) {
[8b1e15ac]549 child_bus = pci_conf_read_8(fun,
[663f41c4]550 PCI_BRIDGE_SEC_BUS_NUM);
[fc51296]551 ddf_msg(LVL_DEBUG, "Device is pci-to-pci "
[ebcb05a]552 "bridge, secondary bus number = %d.",
[fc51296]553 bus_num);
[8304889]554 if (child_bus > bus_num)
[68414f4a]555 pci_bus_scan(bus, child_bus);
[5e598e0]556 }
557
[97a62fe]558 fun = pci_fun_new(bus);
[5e598e0]559 }
560 }
561
[68414f4a]562 if (fun->vendor_id == 0xffff) {
[8b1e15ac]563 /* Free the auxiliary function structure. */
[68414f4a]564 pci_fun_delete(fun);
[663f41c4]565 }
[5e598e0]566}
[8c06905]567
[83a2f43]568static int pci_add_device(ddf_dev_t *dnode)
[8c06905]569{
[97a62fe]570 pci_bus_t *bus = NULL;
[83a2f43]571 ddf_fun_t *ctl = NULL;
[97a62fe]572 bool got_res = false;
[be942bc]573 int rc;
[68414f4a]574
[ebcb05a]575 ddf_msg(LVL_DEBUG, "pci_add_device");
[97a62fe]576 dnode->parent_phone = -1;
[8c06905]577
[97a62fe]578 bus = pci_bus_new();
[68414f4a]579 if (bus == NULL) {
[ebcb05a]580 ddf_msg(LVL_ERROR, "pci_add_device allocation failed.");
[97a62fe]581 rc = ENOMEM;
582 goto fail;
[663f41c4]583 }
[68414f4a]584 bus->dnode = dnode;
585 dnode->driver_data = bus;
[8c06905]586
[68414f4a]587 dnode->parent_phone = devman_parent_device_connect(dnode->handle,
[663f41c4]588 IPC_FLAG_BLOCKING);
[68414f4a]589 if (dnode->parent_phone < 0) {
[fc51296]590 ddf_msg(LVL_ERROR, "pci_add_device failed to connect to the "
[ebcb05a]591 "parent's driver.");
[97a62fe]592 rc = dnode->parent_phone;
593 goto fail;
[8c06905]594 }
595
596 hw_resource_list_t hw_resources;
597
[68414f4a]598 rc = hw_res_get_resource_list(dnode->parent_phone, &hw_resources);
[be942bc]599 if (rc != EOK) {
[fc51296]600 ddf_msg(LVL_ERROR, "pci_add_device failed to get hw resources "
[ebcb05a]601 "for the device.");
[97a62fe]602 goto fail;
[bab6388]603 }
[97a62fe]604 got_res = true;
[8c06905]605
[ebcb05a]606 ddf_msg(LVL_DEBUG, "conf_addr = %" PRIx64 ".",
[663f41c4]607 hw_resources.resources[0].res.io_range.address);
[8c06905]608
609 assert(hw_resources.count > 0);
[3a5909f]610 assert(hw_resources.resources[0].type == IO_RANGE);
611 assert(hw_resources.resources[0].res.io_range.size == 8);
[8c06905]612
[68414f4a]613 bus->conf_io_addr =
[663f41c4]614 (uint32_t) hw_resources.resources[0].res.io_range.address;
[8c06905]615
[68414f4a]616 if (pio_enable((void *)(uintptr_t)bus->conf_io_addr, 8,
617 &bus->conf_addr_port)) {
[ebcb05a]618 ddf_msg(LVL_ERROR, "Failed to enable configuration ports.");
[97a62fe]619 rc = EADDRNOTAVAIL;
620 goto fail;
[8c06905]621 }
[68414f4a]622 bus->conf_data_port = (char *) bus->conf_addr_port + 4;
[8c06905]623
[68414f4a]624 /* Make the bus device more visible. It has no use yet. */
[ebcb05a]625 ddf_msg(LVL_DEBUG, "Adding a 'ctl' function");
[68414f4a]626
[97a62fe]627 ctl = ddf_fun_create(bus->dnode, fun_exposed, "ctl");
628 if (ctl == NULL) {
[ebcb05a]629 ddf_msg(LVL_ERROR, "Failed creating control function.");
[97a62fe]630 rc = ENOMEM;
631 goto fail;
632 }
633
634 rc = ddf_fun_bind(ctl);
635 if (rc != EOK) {
[ebcb05a]636 ddf_msg(LVL_ERROR, "Failed binding control function.");
[97a62fe]637 goto fail;
638 }
[8c06905]639
[68414f4a]640 /* Enumerate functions. */
[ebcb05a]641 ddf_msg(LVL_DEBUG, "Scanning the bus");
[68414f4a]642 pci_bus_scan(bus, 0);
[8c06905]643
[f724e82]644 hw_res_clean_resource_list(&hw_resources);
[8c06905]645
[df747b9c]646 return EOK;
[97a62fe]647
648fail:
649 if (bus != NULL)
650 pci_bus_delete(bus);
651 if (dnode->parent_phone >= 0)
652 async_hangup(dnode->parent_phone);
653 if (got_res)
654 hw_res_clean_resource_list(&hw_resources);
655 if (ctl != NULL)
656 ddf_fun_destroy(ctl);
657
658 return rc;
[8c06905]659}
660
[663f41c4]661static void pciintel_init(void)
[3843ecb]662{
[fc51296]663 ddf_log_init(NAME, LVL_ERROR);
[68414f4a]664 pci_fun_ops.interfaces[HW_RES_DEV_IFACE] = &pciintel_hw_res_ops;
[99e6bfb]665 pci_fun_ops.interfaces[PCI_DEV_IFACE] = &pci_dev_ops;
[3843ecb]666}
667
[97a62fe]668pci_fun_t *pci_fun_new(pci_bus_t *bus)
[713a4b9]669{
[97a62fe]670 pci_fun_t *fun;
[713a4b9]671
[97a62fe]672 fun = (pci_fun_t *) calloc(1, sizeof(pci_fun_t));
673 if (fun == NULL)
674 return NULL;
675
676 fun->busptr = bus;
677 return fun;
[713a4b9]678}
679
[68414f4a]680void pci_fun_init(pci_fun_t *fun, int bus, int dev, int fn)
[713a4b9]681{
[68414f4a]682 fun->bus = bus;
683 fun->dev = dev;
684 fun->fn = fn;
[713a4b9]685}
686
[68414f4a]687void pci_fun_delete(pci_fun_t *fun)
[713a4b9]688{
[bab6388]689 assert(fun != NULL);
690 hw_res_clean_resource_list(&fun->hw_resources);
691 free(fun);
[713a4b9]692}
693
[97a62fe]694char *pci_fun_create_name(pci_fun_t *fun)
[713a4b9]695{
696 char *name = NULL;
697
[68414f4a]698 asprintf(&name, "%02x:%02x.%01x", fun->bus, fun->dev,
699 fun->fn);
[97a62fe]700 return name;
[713a4b9]701}
702
[68414f4a]703bool pci_alloc_resource_list(pci_fun_t *fun)
[713a4b9]704{
[68414f4a]705 fun->hw_resources.resources =
[713a4b9]706 (hw_resource_t *) malloc(PCI_MAX_HW_RES * sizeof(hw_resource_t));
[68414f4a]707 return fun->hw_resources.resources != NULL;
[713a4b9]708}
709
[68414f4a]710void pci_clean_resource_list(pci_fun_t *fun)
[713a4b9]711{
[68414f4a]712 if (fun->hw_resources.resources != NULL) {
713 free(fun->hw_resources.resources);
714 fun->hw_resources.resources = NULL;
[713a4b9]715 }
716}
717
[68414f4a]718/** Read the base address registers (BARs) of the function and add the addresses
719 * to its HW resource list.
[713a4b9]720 *
[68414f4a]721 * @param fun PCI function
[713a4b9]722 */
[68414f4a]723void pci_read_bars(pci_fun_t *fun)
[713a4b9]724{
725 /*
726 * Position of the BAR in the PCI configuration address space of the
727 * device.
728 */
729 int addr = PCI_BASE_ADDR_0;
730
731 while (addr <= PCI_BASE_ADDR_5)
[8b1e15ac]732 addr = pci_read_bar(fun, addr);
[713a4b9]733}
734
735size_t pci_bar_mask_to_size(uint32_t mask)
736{
[ad6857c]737 size_t size = mask & ~(mask - 1);
738 return size;
[713a4b9]739}
740
[8c06905]741int main(int argc, char *argv[])
742{
[ebcb05a]743 printf(NAME ": HelenOS PCI bus driver (Intel method 1).\n");
[3843ecb]744 pciintel_init();
[83a2f43]745 return ddf_driver_main(&pci_driver);
[8c06905]746}
747
748/**
749 * @}
[472020fc]750 */
Note: See TracBrowser for help on using the repository browser.