source: mainline/uspace/drv/bus/pci/pciintel/pci.c@ 3e6a98c5

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 3e6a98c5 was 3e6a98c5, checked in by Jiri Svoboda <jiri@…>, 13 years ago

Standards-compliant boolean type.

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