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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 92d5279 was 92d5279, checked in by Jakub Jermar <jakub@…>, 9 years ago

pciintel: Support alternate method to access PCI configuration space

On UltraSPARC IIi-based machines, the PCI configuration space is mapped
directly in the portion of the PBM (PCI Bus Interface Module) address
space. It is therefore not accessed indirectly via the config address
and config data registers but directly by accessing the respective
portion of the PBM address space.

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