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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since fdaaad00 was 99e8fb7b, checked in by Jan Vesely <jano.vesely@…>, 12 years ago

libdrv: Move pci_config client side to libdrv

Remove duplicate includes, duplicate enum definitions, …

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