00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00039 #include <assert.h>
00040 #include <stdio.h>
00041 #include <errno.h>
00042 #include <bool.h>
00043 #include <fibril_synch.h>
00044 #include <str.h>
00045 #include <ctype.h>
00046 #include <macros.h>
00047 #include <str_error.h>
00048
00049 #include <ddf/driver.h>
00050 #include <ddf/log.h>
00051 #include <devman.h>
00052 #include <ipc/devman.h>
00053 #include <ipc/dev_iface.h>
00054 #include <ipc/irc.h>
00055 #include <ipc/ns.h>
00056 #include <ipc/services.h>
00057 #include <sysinfo.h>
00058 #include <ops/hw_res.h>
00059 #include <device/hw_res.h>
00060 #include <ddi.h>
00061 #include <libarch/ddi.h>
00062 #include <pci_dev_iface.h>
00063
00064 #include "pci.h"
00065
00066 #define NAME "pciintel"
00067
00068 #define CONF_ADDR(bus, dev, fn, reg) \
00069 ((1 << 31) | (bus << 16) | (dev << 11) | (fn << 8) | (reg & ~3))
00070
00072 #define PCI_FUN(fnode) ((pci_fun_t *) (fnode)->driver_data)
00073
00075 #define PCI_BUS(dnode) ((pci_bus_t *) (dnode)->driver_data)
00076
00078 #define PCI_BUS_FROM_FUN(fun) ((fun)->busptr)
00079
00080 static hw_resource_list_t *pciintel_get_resources(ddf_fun_t *fnode)
00081 {
00082 pci_fun_t *fun = PCI_FUN(fnode);
00083
00084 if (fun == NULL)
00085 return NULL;
00086 return &fun->hw_resources;
00087 }
00088
00089 static bool pciintel_enable_interrupt(ddf_fun_t *fnode)
00090 {
00091
00092 assert(fnode);
00093 pci_fun_t *dev_data = (pci_fun_t *) fnode->driver_data;
00094
00095 sysarg_t apic;
00096 sysarg_t i8259;
00097
00098 int irc_phone = ENOTSUP;
00099
00100 if (((sysinfo_get_value("apic", &apic) == EOK) && (apic))
00101 || ((sysinfo_get_value("i8259", &i8259) == EOK) && (i8259))) {
00102 irc_phone = service_connect_blocking(SERVICE_IRC, 0, 0);
00103 }
00104
00105 if (irc_phone < 0) {
00106 return false;
00107 }
00108
00109 size_t i = 0;
00110 hw_resource_list_t *res = &dev_data->hw_resources;
00111 for (; i < res->count; i++) {
00112 if (res->resources[i].type == INTERRUPT) {
00113 const int irq = res->resources[i].res.interrupt.irq;
00114 const int rc =
00115 async_req_1_0(irc_phone, IRC_ENABLE_INTERRUPT, irq);
00116 if (rc != EOK) {
00117 async_hangup(irc_phone);
00118 return false;
00119 }
00120 }
00121 }
00122
00123 async_hangup(irc_phone);
00124 return true;
00125 }
00126
00127 static int pci_config_space_write_32(
00128 ddf_fun_t *fun, uint32_t address, uint32_t data)
00129 {
00130 if (address > 252)
00131 return EINVAL;
00132 pci_conf_write_32(PCI_FUN(fun), address, data);
00133 return EOK;
00134 }
00135
00136 static int pci_config_space_write_16(
00137 ddf_fun_t *fun, uint32_t address, uint16_t data)
00138 {
00139 if (address > 254)
00140 return EINVAL;
00141 pci_conf_write_16(PCI_FUN(fun), address, data);
00142 return EOK;
00143 }
00144
00145 static int pci_config_space_write_8(
00146 ddf_fun_t *fun, uint32_t address, uint8_t data)
00147 {
00148 if (address > 255)
00149 return EINVAL;
00150 pci_conf_write_8(PCI_FUN(fun), address, data);
00151 return EOK;
00152 }
00153
00154 static int pci_config_space_read_32(
00155 ddf_fun_t *fun, uint32_t address, uint32_t *data)
00156 {
00157 if (address > 252)
00158 return EINVAL;
00159 *data = pci_conf_read_32(PCI_FUN(fun), address);
00160 return EOK;
00161 }
00162
00163 static int pci_config_space_read_16(
00164 ddf_fun_t *fun, uint32_t address, uint16_t *data)
00165 {
00166 if (address > 254)
00167 return EINVAL;
00168 *data = pci_conf_read_16(PCI_FUN(fun), address);
00169 return EOK;
00170 }
00171
00172 static int pci_config_space_read_8(
00173 ddf_fun_t *fun, uint32_t address, uint8_t *data)
00174 {
00175 if (address > 255)
00176 return EINVAL;
00177 *data = pci_conf_read_8(PCI_FUN(fun), address);
00178 return EOK;
00179 }
00180
00181 static hw_res_ops_t pciintel_hw_res_ops = {
00182 &pciintel_get_resources,
00183 &pciintel_enable_interrupt
00184 };
00185
00186 static pci_dev_iface_t pci_dev_ops = {
00187 .config_space_read_8 = &pci_config_space_read_8,
00188 .config_space_read_16 = &pci_config_space_read_16,
00189 .config_space_read_32 = &pci_config_space_read_32,
00190 .config_space_write_8 = &pci_config_space_write_8,
00191 .config_space_write_16 = &pci_config_space_write_16,
00192 .config_space_write_32 = &pci_config_space_write_32
00193 };
00194
00195 static ddf_dev_ops_t pci_fun_ops = {
00196 .interfaces[HW_RES_DEV_IFACE] = &pciintel_hw_res_ops,
00197 .interfaces[PCI_DEV_IFACE] = &pci_dev_ops
00198 };
00199
00200 static int pci_add_device(ddf_dev_t *);
00201
00203 static driver_ops_t pci_ops = {
00204 .add_device = &pci_add_device
00205 };
00206
00208 static driver_t pci_driver = {
00209 .name = NAME,
00210 .driver_ops = &pci_ops
00211 };
00212
00213 static pci_bus_t *pci_bus_new(void)
00214 {
00215 pci_bus_t *bus;
00216
00217 bus = (pci_bus_t *) calloc(1, sizeof(pci_bus_t));
00218 if (bus == NULL)
00219 return NULL;
00220
00221 fibril_mutex_initialize(&bus->conf_mutex);
00222 return bus;
00223 }
00224
00225 static void pci_bus_delete(pci_bus_t *bus)
00226 {
00227 assert(bus != NULL);
00228 free(bus);
00229 }
00230
00231 static void pci_conf_read(pci_fun_t *fun, int reg, uint8_t *buf, size_t len)
00232 {
00233 pci_bus_t *bus = PCI_BUS_FROM_FUN(fun);
00234
00235 fibril_mutex_lock(&bus->conf_mutex);
00236
00237 uint32_t conf_addr;
00238 conf_addr = CONF_ADDR(fun->bus, fun->dev, fun->fn, reg);
00239 void *addr = bus->conf_data_port + (reg & 3);
00240
00241 pio_write_32(bus->conf_addr_port, conf_addr);
00242
00243 switch (len) {
00244 case 1:
00245 buf[0] = pio_read_8(addr);
00246 break;
00247 case 2:
00248 ((uint16_t *) buf)[0] = pio_read_16(addr);
00249 break;
00250 case 4:
00251 ((uint32_t *) buf)[0] = pio_read_32(addr);
00252 break;
00253 }
00254
00255 fibril_mutex_unlock(&bus->conf_mutex);
00256 }
00257
00258 static void pci_conf_write(pci_fun_t *fun, int reg, uint8_t *buf, size_t len)
00259 {
00260 pci_bus_t *bus = PCI_BUS_FROM_FUN(fun);
00261
00262 fibril_mutex_lock(&bus->conf_mutex);
00263
00264 uint32_t conf_addr;
00265 conf_addr = CONF_ADDR(fun->bus, fun->dev, fun->fn, reg);
00266 void *addr = bus->conf_data_port + (reg & 3);
00267
00268 pio_write_32(bus->conf_addr_port, conf_addr);
00269
00270 switch (len) {
00271 case 1:
00272 pio_write_8(addr, buf[0]);
00273 break;
00274 case 2:
00275 pio_write_16(addr, ((uint16_t *) buf)[0]);
00276 break;
00277 case 4:
00278 pio_write_32(addr, ((uint32_t *) buf)[0]);
00279 break;
00280 }
00281
00282 fibril_mutex_unlock(&bus->conf_mutex);
00283 }
00284
00285 uint8_t pci_conf_read_8(pci_fun_t *fun, int reg)
00286 {
00287 uint8_t res;
00288 pci_conf_read(fun, reg, &res, 1);
00289 return res;
00290 }
00291
00292 uint16_t pci_conf_read_16(pci_fun_t *fun, int reg)
00293 {
00294 uint16_t res;
00295 pci_conf_read(fun, reg, (uint8_t *) &res, 2);
00296 return res;
00297 }
00298
00299 uint32_t pci_conf_read_32(pci_fun_t *fun, int reg)
00300 {
00301 uint32_t res;
00302 pci_conf_read(fun, reg, (uint8_t *) &res, 4);
00303 return res;
00304 }
00305
00306 void pci_conf_write_8(pci_fun_t *fun, int reg, uint8_t val)
00307 {
00308 pci_conf_write(fun, reg, (uint8_t *) &val, 1);
00309 }
00310
00311 void pci_conf_write_16(pci_fun_t *fun, int reg, uint16_t val)
00312 {
00313 pci_conf_write(fun, reg, (uint8_t *) &val, 2);
00314 }
00315
00316 void pci_conf_write_32(pci_fun_t *fun, int reg, uint32_t val)
00317 {
00318 pci_conf_write(fun, reg, (uint8_t *) &val, 4);
00319 }
00320
00321 void pci_fun_create_match_ids(pci_fun_t *fun)
00322 {
00323 char *match_id_str;
00324 int rc;
00325
00326 asprintf(&match_id_str, "pci/ven=%04x&dev=%04x",
00327 fun->vendor_id, fun->device_id);
00328
00329 if (match_id_str == NULL) {
00330 ddf_msg(LVL_ERROR, "Out of memory creating match ID.");
00331 return;
00332 }
00333
00334 rc = ddf_fun_add_match_id(fun->fnode, match_id_str, 90);
00335 if (rc != EOK) {
00336 ddf_msg(LVL_ERROR, "Failed adding match ID: %s",
00337 str_error(rc));
00338 }
00339
00340
00341 }
00342
00343 void pci_add_range(pci_fun_t *fun, uint64_t range_addr, size_t range_size,
00344 bool io)
00345 {
00346 hw_resource_list_t *hw_res_list = &fun->hw_resources;
00347 hw_resource_t *hw_resources = hw_res_list->resources;
00348 size_t count = hw_res_list->count;
00349
00350 assert(hw_resources != NULL);
00351 assert(count < PCI_MAX_HW_RES);
00352
00353 if (io) {
00354 hw_resources[count].type = IO_RANGE;
00355 hw_resources[count].res.io_range.address = range_addr;
00356 hw_resources[count].res.io_range.size = range_size;
00357 hw_resources[count].res.io_range.endianness = LITTLE_ENDIAN;
00358 } else {
00359 hw_resources[count].type = MEM_RANGE;
00360 hw_resources[count].res.mem_range.address = range_addr;
00361 hw_resources[count].res.mem_range.size = range_size;
00362 hw_resources[count].res.mem_range.endianness = LITTLE_ENDIAN;
00363 }
00364
00365 hw_res_list->count++;
00366 }
00367
00376 int pci_read_bar(pci_fun_t *fun, int addr)
00377 {
00378
00379 uint32_t val, mask;
00380
00381 bool io;
00382
00383 bool addrw64;
00384
00385
00386 size_t range_size;
00387
00388 uint64_t range_addr;
00389
00390
00391 val = pci_conf_read_32(fun, addr);
00392
00393 #define IO_MASK (~0x3)
00394 #define MEM_MASK (~0xf)
00395
00396 io = (bool) (val & 1);
00397 if (io) {
00398 addrw64 = false;
00399 mask = IO_MASK;
00400 } else {
00401 mask = MEM_MASK;
00402 switch ((val >> 1) & 3) {
00403 case 0:
00404 addrw64 = false;
00405 break;
00406 case 2:
00407 addrw64 = true;
00408 break;
00409 default:
00410
00411 return addr + 4;
00412 }
00413 }
00414
00415
00416 pci_conf_write_32(fun, addr, 0xffffffff);
00417 mask &= pci_conf_read_32(fun, addr);
00418
00419
00420 pci_conf_write_32(fun, addr, val);
00421 val = pci_conf_read_32(fun, addr);
00422
00423 range_size = pci_bar_mask_to_size(mask);
00424
00425 if (addrw64) {
00426 range_addr = ((uint64_t)pci_conf_read_32(fun, addr + 4) << 32) |
00427 (val & 0xfffffff0);
00428 } else {
00429 range_addr = (val & 0xfffffff0);
00430 }
00431
00432 if (range_addr != 0) {
00433 ddf_msg(LVL_DEBUG, "Function %s : address = %" PRIx64
00434 ", size = %x", fun->fnode->name, range_addr,
00435 (unsigned int) range_size);
00436 }
00437
00438 pci_add_range(fun, range_addr, range_size, io);
00439
00440 if (addrw64)
00441 return addr + 8;
00442
00443 return addr + 4;
00444 }
00445
00446 void pci_add_interrupt(pci_fun_t *fun, int irq)
00447 {
00448 hw_resource_list_t *hw_res_list = &fun->hw_resources;
00449 hw_resource_t *hw_resources = hw_res_list->resources;
00450 size_t count = hw_res_list->count;
00451
00452 assert(NULL != hw_resources);
00453 assert(count < PCI_MAX_HW_RES);
00454
00455 hw_resources[count].type = INTERRUPT;
00456 hw_resources[count].res.interrupt.irq = irq;
00457
00458 hw_res_list->count++;
00459
00460 ddf_msg(LVL_NOTE, "Function %s uses irq %x.", fun->fnode->name, irq);
00461 }
00462
00463 void pci_read_interrupt(pci_fun_t *fun)
00464 {
00465 uint8_t irq = pci_conf_read_8(fun, PCI_BRIDGE_INT_LINE);
00466 if (irq != 0xff)
00467 pci_add_interrupt(fun, irq);
00468 }
00469
00475 void pci_bus_scan(pci_bus_t *bus, int bus_num)
00476 {
00477 ddf_fun_t *fnode;
00478 pci_fun_t *fun;
00479
00480 int child_bus = 0;
00481 int dnum, fnum;
00482 bool multi;
00483 uint8_t header_type;
00484
00485 fun = pci_fun_new(bus);
00486
00487 for (dnum = 0; dnum < 32; dnum++) {
00488 multi = true;
00489 for (fnum = 0; multi && fnum < 8; fnum++) {
00490 pci_fun_init(fun, bus_num, dnum, fnum);
00491 fun->vendor_id = pci_conf_read_16(fun,
00492 PCI_VENDOR_ID);
00493 fun->device_id = pci_conf_read_16(fun,
00494 PCI_DEVICE_ID);
00495 if (fun->vendor_id == 0xffff) {
00496
00497
00498
00499
00500 if (fnum == 0)
00501 break;
00502 else
00503 continue;
00504 }
00505
00506 header_type = pci_conf_read_8(fun, PCI_HEADER_TYPE);
00507 if (fnum == 0) {
00508
00509 multi = header_type >> 7;
00510 }
00511
00512 header_type = header_type & 0x7F;
00513
00514 char *fun_name = pci_fun_create_name(fun);
00515 if (fun_name == NULL) {
00516 ddf_msg(LVL_ERROR, "Out of memory.");
00517 return;
00518 }
00519
00520 fnode = ddf_fun_create(bus->dnode, fun_inner, fun_name);
00521 if (fnode == NULL) {
00522 ddf_msg(LVL_ERROR, "Failed creating function.");
00523 return;
00524 }
00525
00526 free(fun_name);
00527 fun->fnode = fnode;
00528
00529 pci_alloc_resource_list(fun);
00530 pci_read_bars(fun);
00531 pci_read_interrupt(fun);
00532
00533 fnode->ops = &pci_fun_ops;
00534 fnode->driver_data = fun;
00535
00536 ddf_msg(LVL_DEBUG, "Adding new function %s.",
00537 fnode->name);
00538
00539 pci_fun_create_match_ids(fun);
00540
00541 if (ddf_fun_bind(fnode) != EOK) {
00542 pci_clean_resource_list(fun);
00543 clean_match_ids(&fnode->match_ids);
00544 free((char *) fnode->name);
00545 fnode->name = NULL;
00546 continue;
00547 }
00548
00549 if (header_type == PCI_HEADER_TYPE_BRIDGE ||
00550 header_type == PCI_HEADER_TYPE_CARDBUS) {
00551 child_bus = pci_conf_read_8(fun,
00552 PCI_BRIDGE_SEC_BUS_NUM);
00553 ddf_msg(LVL_DEBUG, "Device is pci-to-pci "
00554 "bridge, secondary bus number = %d.",
00555 bus_num);
00556 if (child_bus > bus_num)
00557 pci_bus_scan(bus, child_bus);
00558 }
00559
00560 fun = pci_fun_new(bus);
00561 }
00562 }
00563
00564 if (fun->vendor_id == 0xffff) {
00565
00566 pci_fun_delete(fun);
00567 }
00568 }
00569
00570 static int pci_add_device(ddf_dev_t *dnode)
00571 {
00572 pci_bus_t *bus = NULL;
00573 ddf_fun_t *ctl = NULL;
00574 bool got_res = false;
00575 int rc;
00576
00577 ddf_msg(LVL_DEBUG, "pci_add_device");
00578 dnode->parent_phone = -1;
00579
00580 bus = pci_bus_new();
00581 if (bus == NULL) {
00582 ddf_msg(LVL_ERROR, "pci_add_device allocation failed.");
00583 rc = ENOMEM;
00584 goto fail;
00585 }
00586 bus->dnode = dnode;
00587 dnode->driver_data = bus;
00588
00589 dnode->parent_phone = devman_parent_device_connect(dnode->handle,
00590 IPC_FLAG_BLOCKING);
00591 if (dnode->parent_phone < 0) {
00592 ddf_msg(LVL_ERROR, "pci_add_device failed to connect to the "
00593 "parent's driver.");
00594 rc = dnode->parent_phone;
00595 goto fail;
00596 }
00597
00598 hw_resource_list_t hw_resources;
00599
00600 rc = hw_res_get_resource_list(dnode->parent_phone, &hw_resources);
00601 if (rc != EOK) {
00602 ddf_msg(LVL_ERROR, "pci_add_device failed to get hw resources "
00603 "for the device.");
00604 goto fail;
00605 }
00606 got_res = true;
00607
00608 ddf_msg(LVL_DEBUG, "conf_addr = %" PRIx64 ".",
00609 hw_resources.resources[0].res.io_range.address);
00610
00611 assert(hw_resources.count > 0);
00612 assert(hw_resources.resources[0].type == IO_RANGE);
00613 assert(hw_resources.resources[0].res.io_range.size == 8);
00614
00615 bus->conf_io_addr =
00616 (uint32_t) hw_resources.resources[0].res.io_range.address;
00617
00618 if (pio_enable((void *)(uintptr_t)bus->conf_io_addr, 8,
00619 &bus->conf_addr_port)) {
00620 ddf_msg(LVL_ERROR, "Failed to enable configuration ports.");
00621 rc = EADDRNOTAVAIL;
00622 goto fail;
00623 }
00624 bus->conf_data_port = (char *) bus->conf_addr_port + 4;
00625
00626
00627 ddf_msg(LVL_DEBUG, "Adding a 'ctl' function");
00628
00629 ctl = ddf_fun_create(bus->dnode, fun_exposed, "ctl");
00630 if (ctl == NULL) {
00631 ddf_msg(LVL_ERROR, "Failed creating control function.");
00632 rc = ENOMEM;
00633 goto fail;
00634 }
00635
00636 rc = ddf_fun_bind(ctl);
00637 if (rc != EOK) {
00638 ddf_msg(LVL_ERROR, "Failed binding control function.");
00639 goto fail;
00640 }
00641
00642
00643 ddf_msg(LVL_DEBUG, "Scanning the bus");
00644 pci_bus_scan(bus, 0);
00645
00646 hw_res_clean_resource_list(&hw_resources);
00647
00648 return EOK;
00649
00650 fail:
00651 if (bus != NULL)
00652 pci_bus_delete(bus);
00653 if (dnode->parent_phone >= 0)
00654 async_hangup(dnode->parent_phone);
00655 if (got_res)
00656 hw_res_clean_resource_list(&hw_resources);
00657 if (ctl != NULL)
00658 ddf_fun_destroy(ctl);
00659
00660 return rc;
00661 }
00662
00663 static void pciintel_init(void)
00664 {
00665 ddf_log_init(NAME, LVL_ERROR);
00666 pci_fun_ops.interfaces[HW_RES_DEV_IFACE] = &pciintel_hw_res_ops;
00667 pci_fun_ops.interfaces[PCI_DEV_IFACE] = &pci_dev_ops;
00668 }
00669
00670 pci_fun_t *pci_fun_new(pci_bus_t *bus)
00671 {
00672 pci_fun_t *fun;
00673
00674 fun = (pci_fun_t *) calloc(1, sizeof(pci_fun_t));
00675 if (fun == NULL)
00676 return NULL;
00677
00678 fun->busptr = bus;
00679 return fun;
00680 }
00681
00682 void pci_fun_init(pci_fun_t *fun, int bus, int dev, int fn)
00683 {
00684 fun->bus = bus;
00685 fun->dev = dev;
00686 fun->fn = fn;
00687 }
00688
00689 void pci_fun_delete(pci_fun_t *fun)
00690 {
00691 assert(fun != NULL);
00692 hw_res_clean_resource_list(&fun->hw_resources);
00693 free(fun);
00694 }
00695
00696 char *pci_fun_create_name(pci_fun_t *fun)
00697 {
00698 char *name = NULL;
00699
00700 asprintf(&name, "%02x:%02x.%01x", fun->bus, fun->dev,
00701 fun->fn);
00702 return name;
00703 }
00704
00705 bool pci_alloc_resource_list(pci_fun_t *fun)
00706 {
00707 fun->hw_resources.resources =
00708 (hw_resource_t *) malloc(PCI_MAX_HW_RES * sizeof(hw_resource_t));
00709 return fun->hw_resources.resources != NULL;
00710 }
00711
00712 void pci_clean_resource_list(pci_fun_t *fun)
00713 {
00714 if (fun->hw_resources.resources != NULL) {
00715 free(fun->hw_resources.resources);
00716 fun->hw_resources.resources = NULL;
00717 }
00718 }
00719
00725 void pci_read_bars(pci_fun_t *fun)
00726 {
00727
00728
00729
00730
00731 int addr = PCI_BASE_ADDR_0;
00732
00733 while (addr <= PCI_BASE_ADDR_5)
00734 addr = pci_read_bar(fun, addr);
00735 }
00736
00737 size_t pci_bar_mask_to_size(uint32_t mask)
00738 {
00739 size_t size = mask & ~(mask - 1);
00740 return size;
00741 }
00742
00743 int main(int argc, char *argv[])
00744 {
00745 printf(NAME ": HelenOS PCI bus driver (Intel method 1).\n");
00746 pciintel_init();
00747 return ddf_driver_main(&pci_driver);
00748 }
00749