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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since f9b2cb4c 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
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>
[ebc9c2c]53#include <irc.h>
[41b56084]54#include <ops/hw_res.h>
[8c06905]55#include <device/hw_res.h>
[6dbc500]56#include <ops/pio_window.h>
57#include <device/pio_window.h>
[8c06905]58#include <ddi.h>
[99e6bfb]59#include <pci_dev_iface.h>
[5e598e0]60
61#include "pci.h"
[8c06905]62
63#define NAME "pciintel"
64
[663f41c4]65#define CONF_ADDR(bus, dev, fn, reg) \
66 ((1 << 31) | (bus << 16) | (dev << 11) | (fn << 8) | (reg & ~3))
[5e598e0]67
[68414f4a]68/** Obtain PCI function soft-state from DDF function node */
[56fd7cf]69static pci_fun_t *pci_fun(ddf_fun_t *fnode)
70{
71 return ddf_fun_data_get(fnode);
72}
[68414f4a]73
74/** Obtain PCI bus soft-state from DDF device node */
[56fd7cf]75#if 0
76static pci_bus_t *pci_bus(ddf_dev_t *dnode)
77{
78 return ddf_dev_data_get(dnode);
79}
80#endif
[68414f4a]81
82/** Obtain PCI bus soft-state from function soft-state */
[56fd7cf]83static pci_bus_t *pci_bus_from_fun(pci_fun_t *fun)
84{
85 return fun->busptr;
86}
[68414f4a]87
[54de4836]88/** Max is 47, align to something nice. */
89#define ID_MAX_STR_LEN 50
90
[83a2f43]91static hw_resource_list_t *pciintel_get_resources(ddf_fun_t *fnode)
[3843ecb]92{
[56fd7cf]93 pci_fun_t *fun = pci_fun(fnode);
[663f41c4]94
[68414f4a]95 if (fun == NULL)
[3843ecb]96 return NULL;
[68414f4a]97 return &fun->hw_resources;
[3843ecb]98}
99
[83a2f43]100static bool pciintel_enable_interrupt(ddf_fun_t *fnode)
[3843ecb]101{
[2df6f6fe]102 /* This is an old ugly way */
[eb1a2f4]103 assert(fnode);
[56fd7cf]104 pci_fun_t *dev_data = pci_fun(fnode);
[79ae36dd]105
[5857be2]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) {
[ebc9c2c]110 int rc = irc_enable_interrupt(
111 res->resources[i].res.interrupt.irq);
[79ae36dd]112
[ebc9c2c]113 if (rc != EOK)
[dc75234]114 return false;
[fb78ae72]115 }
116 }
[79ae36dd]117
[fb78ae72]118 return true;
[3843ecb]119}
120
[6dbc500]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
[99e8fb7b]131static int config_space_write_32(ddf_fun_t *fun, uint32_t address,
[79ae36dd]132 uint32_t data)
[40a5d40]133{
134 if (address > 252)
135 return EINVAL;
[56fd7cf]136 pci_conf_write_32(pci_fun(fun), address, data);
[40a5d40]137 return EOK;
138}
139
[99e8fb7b]140static int config_space_write_16(
[40a5d40]141 ddf_fun_t *fun, uint32_t address, uint16_t data)
[99e6bfb]142{
143 if (address > 254)
144 return EINVAL;
[56fd7cf]145 pci_conf_write_16(pci_fun(fun), address, data);
[99e6bfb]146 return EOK;
147}
148
[99e8fb7b]149static int config_space_write_8(
[40a5d40]150 ddf_fun_t *fun, uint32_t address, uint8_t data)
151{
152 if (address > 255)
153 return EINVAL;
[56fd7cf]154 pci_conf_write_8(pci_fun(fun), address, data);
[40a5d40]155 return EOK;
156}
157
[99e8fb7b]158static int config_space_read_32(
[40a5d40]159 ddf_fun_t *fun, uint32_t address, uint32_t *data)
160{
161 if (address > 252)
162 return EINVAL;
[56fd7cf]163 *data = pci_conf_read_32(pci_fun(fun), address);
[40a5d40]164 return EOK;
165}
166
[99e8fb7b]167static int config_space_read_16(
[40a5d40]168 ddf_fun_t *fun, uint32_t address, uint16_t *data)
169{
170 if (address > 254)
171 return EINVAL;
[56fd7cf]172 *data = pci_conf_read_16(pci_fun(fun), address);
[40a5d40]173 return EOK;
174}
175
[99e8fb7b]176static int config_space_read_8(
[40a5d40]177 ddf_fun_t *fun, uint32_t address, uint8_t *data)
178{
179 if (address > 255)
180 return EINVAL;
[56fd7cf]181 *data = pci_conf_read_8(pci_fun(fun), address);
[40a5d40]182 return EOK;
183}
[99e6bfb]184
[68414f4a]185static hw_res_ops_t pciintel_hw_res_ops = {
[d9cf684a]186 .get_resource_list = &pciintel_get_resources,
187 .enable_interrupt = &pciintel_enable_interrupt,
[3843ecb]188};
189
[6dbc500]190static pio_window_ops_t pciintel_pio_window_ops = {
191 .get_pio_window = &pciintel_get_pio_window
192};
193
[99e6bfb]194static pci_dev_iface_t pci_dev_ops = {
[99e8fb7b]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
[99e6bfb]201};
202
203static ddf_dev_ops_t pci_fun_ops = {
204 .interfaces[HW_RES_DEV_IFACE] = &pciintel_hw_res_ops,
[6dbc500]205 .interfaces[PIO_WINDOW_DEV_IFACE] = &pciintel_pio_window_ops,
[99e6bfb]206 .interfaces[PCI_DEV_IFACE] = &pci_dev_ops
207};
[3843ecb]208
[0c0f823b]209static int pci_dev_add(ddf_dev_t *);
[f278930]210static int pci_fun_online(ddf_fun_t *);
211static int pci_fun_offline(ddf_fun_t *);
[3843ecb]212
[68414f4a]213/** PCI bus driver standard operations */
[8c06905]214static driver_ops_t pci_ops = {
[0c0f823b]215 .dev_add = &pci_dev_add,
[f278930]216 .fun_online = &pci_fun_online,
217 .fun_offline = &pci_fun_offline,
[8c06905]218};
219
[68414f4a]220/** PCI bus driver structure */
[8c06905]221static driver_t pci_driver = {
222 .name = NAME,
223 .driver_ops = &pci_ops
224};
225
[68414f4a]226static void pci_conf_read(pci_fun_t *fun, int reg, uint8_t *buf, size_t len)
[5e598e0]227{
[82721f5]228 const uint32_t conf_addr = CONF_ADDR(fun->bus, fun->dev, fun->fn, reg);
[56fd7cf]229 pci_bus_t *bus = pci_bus_from_fun(fun);
[82721f5]230 uint32_t val;
[5e598e0]231
[68414f4a]232 fibril_mutex_lock(&bus->conf_mutex);
[82721f5]233
[46eb2c4]234 pio_write_32(bus->conf_addr_reg, host2uint32_t_le(conf_addr));
[82721f5]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 */
[46eb2c4]241 val = uint32_t_le2host(pio_read_32(bus->conf_data_reg));
[82721f5]242
[5e598e0]243 switch (len) {
[663f41c4]244 case 1:
[82721f5]245 *buf = (uint8_t) (val >> ((reg & 3) * 8));
[663f41c4]246 break;
247 case 2:
[82721f5]248 *((uint16_t *) buf) = (uint16_t) (val >> ((reg & 3)) * 8);
[663f41c4]249 break;
250 case 4:
[82721f5]251 *((uint32_t *) buf) = (uint32_t) val;
[663f41c4]252 break;
[5e598e0]253 }
254
[68414f4a]255 fibril_mutex_unlock(&bus->conf_mutex);
[5e598e0]256}
257
[68414f4a]258static void pci_conf_write(pci_fun_t *fun, int reg, uint8_t *buf, size_t len)
[d1fc8f0]259{
[82721f5]260 const uint32_t conf_addr = CONF_ADDR(fun->bus, fun->dev, fun->fn, reg);
[56fd7cf]261 pci_bus_t *bus = pci_bus_from_fun(fun);
[b5f716b]262 uint32_t val = 0; // Prevent -Werror=maybe-uninitialized
[d1fc8f0]263
[68414f4a]264 fibril_mutex_lock(&bus->conf_mutex);
[82721f5]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 */
[46eb2c4]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));
[82721f5]279 }
[d1fc8f0]280
281 switch (len) {
[663f41c4]282 case 1:
[82721f5]283 val &= ~(0xffU << ((reg & 3) * 8));
284 val |= *buf << ((reg & 3) * 8);
[663f41c4]285 break;
286 case 2:
[82721f5]287 val &= ~(0xffffU << ((reg & 3) * 8));
288 val |= *((uint16_t *) buf) << ((reg & 3) * 8);
[663f41c4]289 break;
290 case 4:
[82721f5]291 val = *((uint32_t *) buf);
[663f41c4]292 break;
[d1fc8f0]293 }
[82721f5]294
[46eb2c4]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));
[d1fc8f0]297
[68414f4a]298 fibril_mutex_unlock(&bus->conf_mutex);
[d1fc8f0]299}
300
[68414f4a]301uint8_t pci_conf_read_8(pci_fun_t *fun, int reg)
[5e598e0]302{
303 uint8_t res;
[8b1e15ac]304 pci_conf_read(fun, reg, &res, 1);
[5e598e0]305 return res;
306}
307
[68414f4a]308uint16_t pci_conf_read_16(pci_fun_t *fun, int reg)
[5e598e0]309{
310 uint16_t res;
[8b1e15ac]311 pci_conf_read(fun, reg, (uint8_t *) &res, 2);
[5e598e0]312 return res;
313}
314
[68414f4a]315uint32_t pci_conf_read_32(pci_fun_t *fun, int reg)
[5e598e0]316{
317 uint32_t res;
[8b1e15ac]318 pci_conf_read(fun, reg, (uint8_t *) &res, 4);
[663f41c4]319 return res;
[5e598e0]320}
321
[68414f4a]322void pci_conf_write_8(pci_fun_t *fun, int reg, uint8_t val)
[d1fc8f0]323{
[8b1e15ac]324 pci_conf_write(fun, reg, (uint8_t *) &val, 1);
[d1fc8f0]325}
326
[68414f4a]327void pci_conf_write_16(pci_fun_t *fun, int reg, uint16_t val)
[d1fc8f0]328{
[8b1e15ac]329 pci_conf_write(fun, reg, (uint8_t *) &val, 2);
[d1fc8f0]330}
331
[68414f4a]332void pci_conf_write_32(pci_fun_t *fun, int reg, uint32_t val)
[d1fc8f0]333{
[8b1e15ac]334 pci_conf_write(fun, reg, (uint8_t *) &val, 4);
[d1fc8f0]335}
336
[68414f4a]337void pci_fun_create_match_ids(pci_fun_t *fun)
[89ce401a]338{
[cd0684d]339 int rc;
[1d53a78]340 char match_id_str[ID_MAX_STR_LEN];
[cd0684d]341
[1d53a78]342 /* Vendor ID & Device ID, length(incl \0) 22 */
[c90aed4]343 rc = snprintf(match_id_str, ID_MAX_STR_LEN, "pci/ven=%04"
344 PRIx16 "&dev=%04" PRIx16, fun->vendor_id, fun->device_id);
[1d53a78]345 if (rc < 0) {
346 ddf_msg(LVL_ERROR, "Failed creating match ID str: %s",
347 str_error(rc));
[8304889]348 }
349
[cd0684d]350 rc = ddf_fun_add_match_id(fun->fnode, match_id_str, 90);
351 if (rc != EOK) {
[1d53a78]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",
[cd0684d]361 str_error(rc));
[8304889]362 }
[1d53a78]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 */
[89ce401a]411}
412
[68414f4a]413void pci_add_range(pci_fun_t *fun, uint64_t range_addr, size_t range_size,
414 bool io)
[d1fc8f0]415{
[68414f4a]416 hw_resource_list_t *hw_res_list = &fun->hw_resources;
[3a5909f]417 hw_resource_t *hw_resources = hw_res_list->resources;
[663f41c4]418 size_t count = hw_res_list->count;
[3a5909f]419
[8304889]420 assert(hw_resources != NULL);
[3a5909f]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;
[663f41c4]426 hw_resources[count].res.io_range.size = range_size;
[9e470c0]427 hw_resources[count].res.io_range.relative = true;
[663f41c4]428 hw_resources[count].res.io_range.endianness = LITTLE_ENDIAN;
[3a5909f]429 } else {
430 hw_resources[count].type = MEM_RANGE;
431 hw_resources[count].res.mem_range.address = range_addr;
[663f41c4]432 hw_resources[count].res.mem_range.size = range_size;
[9e470c0]433 hw_resources[count].res.mem_range.relative = false;
[3a5909f]434 hw_resources[count].res.mem_range.endianness = LITTLE_ENDIAN;
435 }
436
[663f41c4]437 hw_res_list->count++;
[d1fc8f0]438}
439
[663f41c4]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 *
[68414f4a]443 * @param fun PCI function
[663f41c4]444 * @param addr The address of the BAR in the PCI configuration address space of
[68414f4a]445 * the device
446 * @return The addr the address of the BAR which should be read next
[d1fc8f0]447 */
[68414f4a]448int pci_read_bar(pci_fun_t *fun, int addr)
[bab6388]449{
[663f41c4]450 /* Value of the BAR */
[e8d6ce2]451 uint32_t val;
452 uint32_t bar;
453 uint32_t mask;
454
[663f41c4]455 /* IO space address */
[d1fc8f0]456 bool io;
[663f41c4]457 /* 64-bit wide address */
[d93aafed]458 bool addrw64;
[d1fc8f0]459
[663f41c4]460 /* Size of the io or memory range specified by the BAR */
[d1fc8f0]461 size_t range_size;
[663f41c4]462 /* Beginning of the io or memory range specified by the BAR */
[d1fc8f0]463 uint64_t range_addr;
464
[663f41c4]465 /* Get the value of the BAR. */
[8b1e15ac]466 val = pci_conf_read_32(fun, addr);
[ad6857c]467
468#define IO_MASK (~0x3)
469#define MEM_MASK (~0xf)
[d1fc8f0]470
[663f41c4]471 io = (bool) (val & 1);
[d1fc8f0]472 if (io) {
[d93aafed]473 addrw64 = false;
[ad6857c]474 mask = IO_MASK;
[d1fc8f0]475 } else {
[ad6857c]476 mask = MEM_MASK;
[d1fc8f0]477 switch ((val >> 1) & 3) {
478 case 0:
[d93aafed]479 addrw64 = false;
[d1fc8f0]480 break;
481 case 2:
[d93aafed]482 addrw64 = true;
[d1fc8f0]483 break;
484 default:
[663f41c4]485 /* reserved, go to the next BAR */
486 return addr + 4;
[d1fc8f0]487 }
488 }
489
[663f41c4]490 /* Get the address mask. */
[8b1e15ac]491 pci_conf_write_32(fun, addr, 0xffffffff);
[e8d6ce2]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
[663f41c4]502 /* Restore the original value. */
[8b1e15ac]503 pci_conf_write_32(fun, addr, val);
504 val = pci_conf_read_32(fun, addr);
[d1fc8f0]505
[3a5909f]506 range_size = pci_bar_mask_to_size(mask);
[d1fc8f0]507
[d93aafed]508 if (addrw64) {
[8b1e15ac]509 range_addr = ((uint64_t)pci_conf_read_32(fun, addr + 4) << 32) |
[663f41c4]510 (val & 0xfffffff0);
[d1fc8f0]511 } else {
512 range_addr = (val & 0xfffffff0);
[663f41c4]513 }
514
[d93aafed]515 if (range_addr != 0) {
[fc51296]516 ddf_msg(LVL_DEBUG, "Function %s : address = %" PRIx64
[56fd7cf]517 ", size = %x", ddf_fun_get_name(fun->fnode), range_addr,
[fc51296]518 (unsigned int) range_size);
[d1fc8f0]519 }
520
[8b1e15ac]521 pci_add_range(fun, range_addr, range_size, io);
[d1fc8f0]522
[d93aafed]523 if (addrw64)
[d1fc8f0]524 return addr + 8;
[663f41c4]525
526 return addr + 4;
[d1fc8f0]527}
528
[68414f4a]529void pci_add_interrupt(pci_fun_t *fun, int irq)
[d1fc8f0]530{
[68414f4a]531 hw_resource_list_t *hw_res_list = &fun->hw_resources;
[663f41c4]532 hw_resource_t *hw_resources = hw_res_list->resources;
533 size_t count = hw_res_list->count;
[d1fc8f0]534
[3a5909f]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
[663f41c4]541 hw_res_list->count++;
[3a5909f]542
[56fd7cf]543 ddf_msg(LVL_NOTE, "Function %s uses irq %x.", ddf_fun_get_name(fun->fnode), irq);
[3a5909f]544}
545
[68414f4a]546void pci_read_interrupt(pci_fun_t *fun)
[3a5909f]547{
[8b1e15ac]548 uint8_t irq = pci_conf_read_8(fun, PCI_BRIDGE_INT_LINE);
[65f77f4]549 uint8_t pin = pci_conf_read_8(fun, PCI_BRIDGE_INT_PIN);
550
551 if (pin != 0 && irq != 0xff)
[8b1e15ac]552 pci_add_interrupt(fun, irq);
[d1fc8f0]553}
554
555/** Enumerate (recursively) and register the devices connected to a pci bus.
[663f41c4]556 *
[68414f4a]557 * @param bus Host-to-PCI bridge
558 * @param bus_num Bus number
[d1fc8f0]559 */
[68414f4a]560void pci_bus_scan(pci_bus_t *bus, int bus_num)
[5e598e0]561{
[97a62fe]562 pci_fun_t *fun;
[56fd7cf]563 int rc;
[5e598e0]564
565 int child_bus = 0;
566 int dnum, fnum;
567 bool multi;
[8b1e15ac]568 uint8_t header_type;
[bab6388]569
[5e598e0]570 for (dnum = 0; dnum < 32; dnum++) {
571 multi = true;
572 for (fnum = 0; multi && fnum < 8; fnum++) {
[56fd7cf]573 fun = pci_fun_new(bus);
574
[68414f4a]575 pci_fun_init(fun, bus_num, dnum, fnum);
576 if (fun->vendor_id == 0xffff) {
[56fd7cf]577 pci_fun_delete(fun);
[663f41c4]578 /*
579 * The device is not present, go on scanning the
580 * bus.
581 */
582 if (fnum == 0)
[5e598e0]583 break;
[663f41c4]584 else
585 continue;
[5e598e0]586 }
[663f41c4]587
[8b1e15ac]588 header_type = pci_conf_read_8(fun, PCI_HEADER_TYPE);
[5e598e0]589 if (fnum == 0) {
[663f41c4]590 /* Is the device multifunction? */
591 multi = header_type >> 7;
[5e598e0]592 }
[663f41c4]593 /* Clear the multifunction bit. */
594 header_type = header_type & 0x7F;
[5e598e0]595
[97a62fe]596 char *fun_name = pci_fun_create_name(fun);
597 if (fun_name == NULL) {
[ebcb05a]598 ddf_msg(LVL_ERROR, "Out of memory.");
[56fd7cf]599 pci_fun_delete(fun);
[97a62fe]600 return;
601 }
602
[56fd7cf]603 rc = ddf_fun_set_name(fun->fnode, fun_name);
[cb94e69b]604 free(fun_name);
[56fd7cf]605 if (rc != EOK) {
606 ddf_msg(LVL_ERROR, "Failed setting function name.");
607 pci_fun_delete(fun);
[97a62fe]608 return;
609 }
[3a5909f]610
[8b1e15ac]611 pci_alloc_resource_list(fun);
612 pci_read_bars(fun);
613 pci_read_interrupt(fun);
[6dbc500]614
615 /* Propagate the PIO window to the function. */
616 fun->pio_window = bus->pio_win;
[89ce401a]617
[56fd7cf]618 ddf_fun_set_ops(fun->fnode, &pci_fun_ops);
[89ce401a]619
[ebcb05a]620 ddf_msg(LVL_DEBUG, "Adding new function %s.",
[56fd7cf]621 ddf_fun_get_name(fun->fnode));
[89ce401a]622
[68414f4a]623 pci_fun_create_match_ids(fun);
[89ce401a]624
[56fd7cf]625 if (ddf_fun_bind(fun->fnode) != EOK) {
[8b1e15ac]626 pci_clean_resource_list(fun);
[56fd7cf]627 pci_fun_delete(fun);
[89ce401a]628 continue;
629 }
[5e598e0]630
[663f41c4]631 if (header_type == PCI_HEADER_TYPE_BRIDGE ||
[8304889]632 header_type == PCI_HEADER_TYPE_CARDBUS) {
[8b1e15ac]633 child_bus = pci_conf_read_8(fun,
[663f41c4]634 PCI_BRIDGE_SEC_BUS_NUM);
[fc51296]635 ddf_msg(LVL_DEBUG, "Device is pci-to-pci "
[ebcb05a]636 "bridge, secondary bus number = %d.",
[fc51296]637 bus_num);
[8304889]638 if (child_bus > bus_num)
[68414f4a]639 pci_bus_scan(bus, child_bus);
[5e598e0]640 }
641 }
642 }
643}
[8c06905]644
[0c0f823b]645static int pci_dev_add(ddf_dev_t *dnode)
[8c06905]646{
[6dbc500]647 hw_resource_list_t hw_resources;
[97a62fe]648 pci_bus_t *bus = NULL;
[83a2f43]649 ddf_fun_t *ctl = NULL;
[97a62fe]650 bool got_res = false;
[56fd7cf]651 async_sess_t *sess;
[be942bc]652 int rc;
[68414f4a]653
[0c0f823b]654 ddf_msg(LVL_DEBUG, "pci_dev_add");
[8c06905]655
[5f6e25e]656 bus = ddf_dev_data_alloc(dnode, sizeof(pci_bus_t));
[68414f4a]657 if (bus == NULL) {
[0c0f823b]658 ddf_msg(LVL_ERROR, "pci_dev_add allocation failed.");
[97a62fe]659 rc = ENOMEM;
660 goto fail;
[663f41c4]661 }
[5f6e25e]662 fibril_mutex_initialize(&bus->conf_mutex);
663
[68414f4a]664 bus->dnode = dnode;
[8c06905]665
[f9b2cb4c]666 sess = ddf_dev_parent_sess_create(dnode);
[56fd7cf]667 if (sess == NULL) {
[0c0f823b]668 ddf_msg(LVL_ERROR, "pci_dev_add failed to connect to the "
[79ae36dd]669 "parent driver.");
670 rc = ENOENT;
[97a62fe]671 goto fail;
[8c06905]672 }
[6dbc500]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 }
[8c06905]680
[56fd7cf]681 rc = hw_res_get_resource_list(sess, &hw_resources);
[be942bc]682 if (rc != EOK) {
[0c0f823b]683 ddf_msg(LVL_ERROR, "pci_dev_add failed to get hw resources "
[ebcb05a]684 "for the device.");
[97a62fe]685 goto fail;
[bab6388]686 }
[97a62fe]687 got_res = true;
[8c06905]688
689
[230385c]690 assert(hw_resources.count > 1);
[3a5909f]691 assert(hw_resources.resources[0].type == IO_RANGE);
[230385c]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);
[8c06905]701
[46eb2c4]702 if (pio_enable_resource(&bus->pio_win, &hw_resources.resources[0],
703 (void **) &bus->conf_addr_reg)) {
[ebcb05a]704 ddf_msg(LVL_ERROR, "Failed to enable configuration ports.");
[97a62fe]705 rc = EADDRNOTAVAIL;
706 goto fail;
[8c06905]707 }
[46eb2c4]708 if (pio_enable_resource(&bus->pio_win, &hw_resources.resources[1],
709 (void **) &bus->conf_data_reg)) {
[230385c]710 ddf_msg(LVL_ERROR, "Failed to enable configuration ports.");
711 rc = EADDRNOTAVAIL;
712 goto fail;
713 }
[8c06905]714
[68414f4a]715 /* Make the bus device more visible. It has no use yet. */
[ebcb05a]716 ddf_msg(LVL_DEBUG, "Adding a 'ctl' function");
[68414f4a]717
[97a62fe]718 ctl = ddf_fun_create(bus->dnode, fun_exposed, "ctl");
719 if (ctl == NULL) {
[ebcb05a]720 ddf_msg(LVL_ERROR, "Failed creating control function.");
[97a62fe]721 rc = ENOMEM;
722 goto fail;
723 }
724
725 rc = ddf_fun_bind(ctl);
726 if (rc != EOK) {
[ebcb05a]727 ddf_msg(LVL_ERROR, "Failed binding control function.");
[97a62fe]728 goto fail;
729 }
[8c06905]730
[68414f4a]731 /* Enumerate functions. */
[ebcb05a]732 ddf_msg(LVL_DEBUG, "Scanning the bus");
[68414f4a]733 pci_bus_scan(bus, 0);
[8c06905]734
[f724e82]735 hw_res_clean_resource_list(&hw_resources);
[8c06905]736
[df747b9c]737 return EOK;
[97a62fe]738
739fail:
740 if (got_res)
741 hw_res_clean_resource_list(&hw_resources);
[79ae36dd]742
[97a62fe]743 if (ctl != NULL)
744 ddf_fun_destroy(ctl);
[79ae36dd]745
[97a62fe]746 return rc;
[8c06905]747}
748
[f278930]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
[663f41c4]761static void pciintel_init(void)
[3843ecb]762{
[267f235]763 ddf_log_init(NAME);
[3843ecb]764}
765
[97a62fe]766pci_fun_t *pci_fun_new(pci_bus_t *bus)
[713a4b9]767{
[97a62fe]768 pci_fun_t *fun;
[56fd7cf]769 ddf_fun_t *fnode;
[713a4b9]770
[56fd7cf]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));
[97a62fe]776 if (fun == NULL)
777 return NULL;
778
779 fun->busptr = bus;
[56fd7cf]780 fun->fnode = fnode;
[97a62fe]781 return fun;
[713a4b9]782}
783
[68414f4a]784void pci_fun_init(pci_fun_t *fun, int bus, int dev, int fn)
[713a4b9]785{
[68414f4a]786 fun->bus = bus;
787 fun->dev = dev;
788 fun->fn = fn;
[1d53a78]789 fun->vendor_id = pci_conf_read_16(fun, PCI_VENDOR_ID);
790 fun->device_id = pci_conf_read_16(fun, PCI_DEVICE_ID);
[c90aed4]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
[1d53a78]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);
[713a4b9]801}
802
[68414f4a]803void pci_fun_delete(pci_fun_t *fun)
[713a4b9]804{
[bab6388]805 hw_res_clean_resource_list(&fun->hw_resources);
[56fd7cf]806 if (fun->fnode != NULL)
807 ddf_fun_destroy(fun->fnode);
[713a4b9]808}
809
[97a62fe]810char *pci_fun_create_name(pci_fun_t *fun)
[713a4b9]811{
812 char *name = NULL;
813
[68414f4a]814 asprintf(&name, "%02x:%02x.%01x", fun->bus, fun->dev,
815 fun->fn);
[97a62fe]816 return name;
[713a4b9]817}
818
[68414f4a]819bool pci_alloc_resource_list(pci_fun_t *fun)
[713a4b9]820{
[992b47ea]821 fun->hw_resources.resources = fun->resources;
822 return true;
[713a4b9]823}
824
[68414f4a]825void pci_clean_resource_list(pci_fun_t *fun)
[713a4b9]826{
[992b47ea]827 fun->hw_resources.resources = NULL;
[713a4b9]828}
829
[68414f4a]830/** Read the base address registers (BARs) of the function and add the addresses
831 * to its HW resource list.
[713a4b9]832 *
[68414f4a]833 * @param fun PCI function
[713a4b9]834 */
[68414f4a]835void pci_read_bars(pci_fun_t *fun)
[713a4b9]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)
[8b1e15ac]844 addr = pci_read_bar(fun, addr);
[713a4b9]845}
846
847size_t pci_bar_mask_to_size(uint32_t mask)
848{
[ad6857c]849 size_t size = mask & ~(mask - 1);
850 return size;
[713a4b9]851}
852
[8c06905]853int main(int argc, char *argv[])
854{
[ebcb05a]855 printf(NAME ": HelenOS PCI bus driver (Intel method 1).\n");
[3843ecb]856 pciintel_init();
[83a2f43]857 return ddf_driver_main(&pci_driver);
[8c06905]858}
859
860/**
861 * @}
[472020fc]862 */
Note: See TracBrowser for help on using the repository browser.