source: mainline/uspace/drv/pciintel/pci.c@ 663f41c4

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

Cstyle fixes in pciintel.

  • Property mode set to 100644
File size: 13.1 KB
RevLine 
[8c06905]1/*
2 * Copyright (c) 2010 Lenka Trochtova
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/**
30 * @defgroup pciintel pci bus driver for intel method 1.
31 * @brief HelenOS root pci bus driver for intel method 1.
32 * @{
33 */
34
35/** @file
36 */
37
38#include <assert.h>
39#include <stdio.h>
40#include <errno.h>
41#include <bool.h>
42#include <fibril_synch.h>
[c47e1a8]43#include <str.h>
[8c06905]44#include <ctype.h>
45#include <macros.h>
46
47#include <driver.h>
48#include <devman.h>
49#include <ipc/devman.h>
50#include <ipc/dev_iface.h>
51#include <resource.h>
52#include <device/hw_res.h>
53#include <ddi.h>
[5e598e0]54#include <libarch/ddi.h>
55
56#include "pci.h"
[8c06905]57
58#define NAME "pciintel"
59
[663f41c4]60#define CONF_ADDR(bus, dev, fn, reg) \
61 ((1 << 31) | (bus << 16) | (dev << 11) | (fn << 8) | (reg & ~3))
[5e598e0]62
[663f41c4]63static hw_resource_list_t *pciintel_get_child_resources(device_t *dev)
[3843ecb]64{
[663f41c4]65 pci_dev_data_t *dev_data = (pci_dev_data_t *) dev->driver_data;
66
67 if (NULL == dev_data)
[3843ecb]68 return NULL;
69 return &dev_data->hw_resources;
70}
71
[663f41c4]72static bool pciintel_enable_child_interrupt(device_t *dev)
[3843ecb]73{
[663f41c4]74 /* TODO */
[3843ecb]75
76 return false;
77}
78
79static resource_iface_t pciintel_child_res_iface = {
80 &pciintel_get_child_resources,
[663f41c4]81 &pciintel_enable_child_interrupt
[3843ecb]82};
83
[5159ae9]84static device_ops_t pci_child_ops;
[3843ecb]85
[663f41c4]86static int pci_add_device(device_t *);
[3843ecb]87
[663f41c4]88/** The pci bus driver's standard operations. */
[8c06905]89static driver_ops_t pci_ops = {
90 .add_device = &pci_add_device
91};
92
[663f41c4]93/** The pci bus driver structure. */
[8c06905]94static driver_t pci_driver = {
95 .name = NAME,
96 .driver_ops = &pci_ops
97};
98
99typedef struct pciintel_bus_data {
[d1fc8f0]100 uint32_t conf_io_addr;
[8c06905]101 void *conf_data_port;
[663f41c4]102 void *conf_addr_port;
[5e598e0]103 fibril_mutex_t conf_mutex;
[8c06905]104} pci_bus_data_t;
105
[663f41c4]106static pci_bus_data_t *create_pci_bus_data(void)
[5e598e0]107{
[663f41c4]108 pci_bus_data_t *bus_data;
109
110 bus_data = (pci_bus_data_t *) malloc(sizeof(pci_bus_data_t));
111 if (NULL != bus_data) {
[5e598e0]112 memset(bus_data, 0, sizeof(pci_bus_data_t));
113 fibril_mutex_initialize(&bus_data->conf_mutex);
114 }
[663f41c4]115 return bus_data;
[5e598e0]116}
117
[663f41c4]118static void delete_pci_bus_data(pci_bus_data_t *bus_data)
[5e598e0]119{
[663f41c4]120 free(bus_data);
[5e598e0]121}
122
123static void pci_conf_read(device_t *dev, int reg, uint8_t *buf, size_t len)
124{
125 assert(NULL != dev->parent);
126
[663f41c4]127 pci_dev_data_t *dev_data = (pci_dev_data_t *) dev->driver_data;
128 pci_bus_data_t *bus_data = (pci_bus_data_t *) dev->parent->driver_data;
[5e598e0]129
130 fibril_mutex_lock(&bus_data->conf_mutex);
131
[663f41c4]132 uint32_t conf_addr;
133 conf_addr = CONF_ADDR(dev_data->bus, dev_data->dev, dev_data->fn, reg);
[5e598e0]134 void *addr = bus_data->conf_data_port + (reg & 3);
135
136 pio_write_32(bus_data->conf_addr_port, conf_addr);
137
138 switch (len) {
[663f41c4]139 case 1:
140 buf[0] = pio_read_8(addr);
141 break;
142 case 2:
143 ((uint16_t *) buf)[0] = pio_read_16(addr);
144 break;
145 case 4:
146 ((uint32_t *) buf)[0] = pio_read_32(addr);
147 break;
[5e598e0]148 }
149
[663f41c4]150 fibril_mutex_unlock(&bus_data->conf_mutex);
[5e598e0]151}
152
[d1fc8f0]153static void pci_conf_write(device_t *dev, int reg, uint8_t *buf, size_t len)
154{
155 assert(NULL != dev->parent);
156
[663f41c4]157 pci_dev_data_t *dev_data = (pci_dev_data_t *) dev->driver_data;
158 pci_bus_data_t *bus_data = (pci_bus_data_t *) dev->parent->driver_data;
[d1fc8f0]159
160 fibril_mutex_lock(&bus_data->conf_mutex);
161
[663f41c4]162 uint32_t conf_addr;
163 conf_addr = CONF_ADDR(dev_data->bus, dev_data->dev, dev_data->fn, reg);
[d1fc8f0]164 void *addr = bus_data->conf_data_port + (reg & 3);
165
166 pio_write_32(bus_data->conf_addr_port, conf_addr);
167
168 switch (len) {
[663f41c4]169 case 1:
170 pio_write_8(addr, buf[0]);
171 break;
172 case 2:
173 pio_write_16(addr, ((uint16_t *) buf)[0]);
174 break;
175 case 4:
176 pio_write_32(addr, ((uint32_t *) buf)[0]);
177 break;
[d1fc8f0]178 }
179
[663f41c4]180 fibril_mutex_unlock(&bus_data->conf_mutex);
[d1fc8f0]181}
182
[5e598e0]183uint8_t pci_conf_read_8(device_t *dev, int reg)
184{
185 uint8_t res;
186 pci_conf_read(dev, reg, &res, 1);
187 return res;
188}
189
190uint16_t pci_conf_read_16(device_t *dev, int reg)
191{
192 uint16_t res;
[663f41c4]193 pci_conf_read(dev, reg, (uint8_t *) &res, 2);
[5e598e0]194 return res;
195}
196
197uint32_t pci_conf_read_32(device_t *dev, int reg)
198{
199 uint32_t res;
[663f41c4]200 pci_conf_read(dev, reg, (uint8_t *) &res, 4);
201 return res;
[5e598e0]202}
203
[663f41c4]204void pci_conf_write_8(device_t *dev, int reg, uint8_t val)
[d1fc8f0]205{
[663f41c4]206 pci_conf_write(dev, reg, (uint8_t *) &val, 1);
[d1fc8f0]207}
208
[663f41c4]209void pci_conf_write_16(device_t *dev, int reg, uint16_t val)
[d1fc8f0]210{
[663f41c4]211 pci_conf_write(dev, reg, (uint8_t *) &val, 2);
[d1fc8f0]212}
213
[663f41c4]214void pci_conf_write_32(device_t *dev, int reg, uint32_t val)
[d1fc8f0]215{
[663f41c4]216 pci_conf_write(dev, reg, (uint8_t *) &val, 4);
[d1fc8f0]217}
218
[89ce401a]219void create_pci_match_ids(device_t *dev)
220{
[663f41c4]221 pci_dev_data_t *dev_data = (pci_dev_data_t *) dev->driver_data;
222 match_id_t *match_id = NULL;
223 char *match_id_str;
224
[89ce401a]225 match_id = create_match_id();
226 if (NULL != match_id) {
[663f41c4]227 asprintf(&match_id_str, "pci/ven=%04x&dev=%04x",
228 dev_data->vendor_id, dev_data->device_id);
[c47e1a8]229 match_id->id = match_id_str;
[89ce401a]230 match_id->score = 90;
231 add_match_id(&dev->match_ids, match_id);
232 }
[663f41c4]233 /* TODO add more ids (with subsys ids, using class id etc.) */
[89ce401a]234}
235
[663f41c4]236void
237pci_add_range(device_t *dev, uint64_t range_addr, size_t range_size, bool io)
[d1fc8f0]238{
[663f41c4]239 pci_dev_data_t *dev_data = (pci_dev_data_t *) dev->driver_data;
[3a5909f]240 hw_resource_list_t *hw_res_list = &dev_data->hw_resources;
241 hw_resource_t *hw_resources = hw_res_list->resources;
[663f41c4]242 size_t count = hw_res_list->count;
[3a5909f]243
244 assert(NULL != hw_resources);
245 assert(count < PCI_MAX_HW_RES);
246
247 if (io) {
248 hw_resources[count].type = IO_RANGE;
249 hw_resources[count].res.io_range.address = range_addr;
[663f41c4]250 hw_resources[count].res.io_range.size = range_size;
251 hw_resources[count].res.io_range.endianness = LITTLE_ENDIAN;
[3a5909f]252 } else {
253 hw_resources[count].type = MEM_RANGE;
254 hw_resources[count].res.mem_range.address = range_addr;
[663f41c4]255 hw_resources[count].res.mem_range.size = range_size;
[3a5909f]256 hw_resources[count].res.mem_range.endianness = LITTLE_ENDIAN;
257 }
258
[663f41c4]259 hw_res_list->count++;
[d1fc8f0]260}
261
[663f41c4]262/** Read the base address register (BAR) of the device and if it contains valid
263 * address add it to the devices hw resource list.
264 *
265 * @param dev The pci device.
266 * @param addr The address of the BAR in the PCI configuration address space of
267 * the device.
268 * @return The addr the address of the BAR which should be read next.
[d1fc8f0]269 */
[663f41c4]270int pci_read_bar(device_t *dev, int addr)
[d1fc8f0]271{
[663f41c4]272 /* Value of the BAR */
[d1fc8f0]273 uint32_t val, mask;
[663f41c4]274 /* IO space address */
[d1fc8f0]275 bool io;
[663f41c4]276 /* 64-bit wide address */
[d1fc8f0]277 bool w64;
278
[663f41c4]279 /* Size of the io or memory range specified by the BAR */
[d1fc8f0]280 size_t range_size;
[663f41c4]281 /* Beginning of the io or memory range specified by the BAR */
[d1fc8f0]282 uint64_t range_addr;
283
[663f41c4]284 /* Get the value of the BAR. */
[d1fc8f0]285 val = pci_conf_read_32(dev, addr);
286
[663f41c4]287 io = (bool) (val & 1);
[d1fc8f0]288 if (io) {
289 w64 = false;
290 } else {
291 switch ((val >> 1) & 3) {
292 case 0:
293 w64 = false;
294 break;
295 case 2:
296 w64 = true;
297 break;
298 default:
[663f41c4]299 /* reserved, go to the next BAR */
300 return addr + 4;
[d1fc8f0]301 }
302 }
303
[663f41c4]304 /* Get the address mask. */
[d1fc8f0]305 pci_conf_write_32(dev, addr, 0xffffffff);
[663f41c4]306 mask = pci_conf_read_32(dev, addr);
[d1fc8f0]307
[663f41c4]308 /* Restore the original value. */
[d1fc8f0]309 pci_conf_write_32(dev, addr, val);
[663f41c4]310 val = pci_conf_read_32(dev, addr);
[d1fc8f0]311
[3a5909f]312 range_size = pci_bar_mask_to_size(mask);
[d1fc8f0]313
314 if (w64) {
[663f41c4]315 range_addr = ((uint64_t)pci_conf_read_32(dev, addr + 4) << 32) |
316 (val & 0xfffffff0);
[d1fc8f0]317 } else {
318 range_addr = (val & 0xfffffff0);
[663f41c4]319 }
320
[d1fc8f0]321 if (0 != range_addr) {
[3a5909f]322 printf(NAME ": device %s : ", dev->name);
[663f41c4]323 printf("address = %x", range_addr);
[3a5909f]324 printf(", size = %x\n", range_size);
[d1fc8f0]325 }
326
[3a5909f]327 pci_add_range(dev, range_addr, range_size, io);
[d1fc8f0]328
[663f41c4]329 if (w64)
[d1fc8f0]330 return addr + 8;
[663f41c4]331
332 return addr + 4;
[d1fc8f0]333}
334
[3a5909f]335void pci_add_interrupt(device_t *dev, int irq)
[d1fc8f0]336{
[663f41c4]337 pci_dev_data_t *dev_data = (pci_dev_data_t *) dev->driver_data;
[3a5909f]338 hw_resource_list_t *hw_res_list = &dev_data->hw_resources;
[663f41c4]339 hw_resource_t *hw_resources = hw_res_list->resources;
340 size_t count = hw_res_list->count;
[d1fc8f0]341
[3a5909f]342 assert(NULL != hw_resources);
343 assert(count < PCI_MAX_HW_RES);
344
345 hw_resources[count].type = INTERRUPT;
346 hw_resources[count].res.interrupt.irq = irq;
347
[663f41c4]348 hw_res_list->count++;
[3a5909f]349
350 printf(NAME ": device %s uses irq %x.\n", dev->name, irq);
351}
352
353void pci_read_interrupt(device_t *dev)
354{
355 uint8_t irq = pci_conf_read_8(dev, PCI_BRIDGE_INT_LINE);
[663f41c4]356 if (0xff != irq)
[3a5909f]357 pci_add_interrupt(dev, irq);
[d1fc8f0]358}
359
360/** Enumerate (recursively) and register the devices connected to a pci bus.
[663f41c4]361 *
362 * @param parent The host-to-pci bridge device.
363 * @param bus_num The bus number.
[d1fc8f0]364 */
[5e598e0]365void pci_bus_scan(device_t *parent, int bus_num)
366{
367 device_t *dev = create_device();
368 pci_dev_data_t *dev_data = create_pci_dev_data();
369 dev->driver_data = dev_data;
370 dev->parent = parent;
371
372 int child_bus = 0;
373 int dnum, fnum;
374 bool multi;
[d1fc8f0]375 uint8_t header_type;
[5e598e0]376
377 for (dnum = 0; dnum < 32; dnum++) {
378 multi = true;
379 for (fnum = 0; multi && fnum < 8; fnum++) {
380 init_pci_dev_data(dev_data, bus_num, dnum, fnum);
[663f41c4]381 dev_data->vendor_id = pci_conf_read_16(dev,
382 PCI_VENDOR_ID);
383 dev_data->device_id = pci_conf_read_16(dev,
384 PCI_DEVICE_ID);
385 if (dev_data->vendor_id == 0xffff) {
386 /*
387 * The device is not present, go on scanning the
388 * bus.
389 */
390 if (fnum == 0)
[5e598e0]391 break;
[663f41c4]392 else
393 continue;
[5e598e0]394 }
[663f41c4]395
[5e598e0]396 header_type = pci_conf_read_8(dev, PCI_HEADER_TYPE);
397 if (fnum == 0) {
[663f41c4]398 /* Is the device multifunction? */
399 multi = header_type >> 7;
[5e598e0]400 }
[663f41c4]401 /* Clear the multifunction bit. */
402 header_type = header_type & 0x7F;
[5e598e0]403
[3a5909f]404 create_pci_dev_name(dev);
405
406 pci_alloc_resource_list(dev);
[d1fc8f0]407 pci_read_bars(dev);
[3a5909f]408 pci_read_interrupt(dev);
409
[663f41c4]410 dev->ops = &pci_child_ops;
[89ce401a]411
[663f41c4]412 printf(NAME ": adding new child device %s.\n",
413 dev->name);
[89ce401a]414
415 create_pci_match_ids(dev);
416
[663f41c4]417 if (EOK != child_device_register(dev, parent)) {
418 pci_clean_resource_list(dev);
[89ce401a]419 clean_match_ids(&dev->match_ids);
[663f41c4]420 free((char *) dev->name);
[89ce401a]421 dev->name = NULL;
422 continue;
423 }
[5e598e0]424
[663f41c4]425 if (header_type == PCI_HEADER_TYPE_BRIDGE ||
426 header_type == PCI_HEADER_TYPE_CARDBUS ) {
427 child_bus = pci_conf_read_8(dev,
428 PCI_BRIDGE_SEC_BUS_NUM);
429 printf(NAME ": device is pci-to-pci bridge, "
430 "secondary bus number = %d.\n", bus_num);
431 if(child_bus > bus_num)
432 pci_bus_scan(parent, child_bus);
[5e598e0]433 }
434
[663f41c4]435 /* Alloc new aux. dev. structure. */
436 dev = create_device();
[5e598e0]437 dev_data = create_pci_dev_data();
438 dev->driver_data = dev_data;
439 dev->parent = parent;
440 }
441 }
442
[3a5909f]443 if (dev_data->vendor_id == 0xffff) {
[5e598e0]444 delete_device(dev);
[663f41c4]445 /* Free the auxiliary device structure. */
446 delete_pci_dev_data(dev_data);
447 }
[5e598e0]448}
[8c06905]449
[df747b9c]450static int pci_add_device(device_t *dev)
[8c06905]451{
452 printf(NAME ": pci_add_device\n");
453
[5e598e0]454 pci_bus_data_t *bus_data = create_pci_bus_data();
[8c06905]455 if (NULL == bus_data) {
456 printf(NAME ": pci_add_device allocation failed.\n");
[df747b9c]457 return ENOMEM;
[663f41c4]458 }
[8c06905]459
[663f41c4]460 dev->parent_phone = devman_parent_device_connect(dev->handle,
461 IPC_FLAG_BLOCKING);
462 if (dev->parent_phone < 0) {
463 printf(NAME ": pci_add_device failed to connect to the "
464 "parent's driver.\n");
[89ce401a]465 delete_pci_bus_data(bus_data);
[472020fc]466 return EPARTY; /* FIXME: use another EC */
[8c06905]467 }
468
469 hw_resource_list_t hw_resources;
470
471 if (!get_hw_resources(dev->parent_phone, &hw_resources)) {
[663f41c4]472 printf(NAME ": pci_add_device failed to get hw resources for "
473 "the device.\n");
[89ce401a]474 delete_pci_bus_data(bus_data);
[8c06905]475 ipc_hangup(dev->parent_phone);
[472020fc]476 return EPARTY; /* FIXME: use another EC */
[3a5909f]477 }
[8c06905]478
[663f41c4]479 printf(NAME ": conf_addr = %x.\n",
480 hw_resources.resources[0].res.io_range.address);
[8c06905]481
482 assert(hw_resources.count > 0);
[3a5909f]483 assert(hw_resources.resources[0].type == IO_RANGE);
484 assert(hw_resources.resources[0].res.io_range.size == 8);
[8c06905]485
[663f41c4]486 bus_data->conf_io_addr =
487 (uint32_t) hw_resources.resources[0].res.io_range.address;
[8c06905]488
[663f41c4]489 if (pio_enable((void *)bus_data->conf_io_addr, 8,
490 &bus_data->conf_addr_port)) {
[8c06905]491 printf(NAME ": failed to enable configuration ports.\n");
[89ce401a]492 delete_pci_bus_data(bus_data);
[8c06905]493 ipc_hangup(dev->parent_phone);
494 clean_hw_resource_list(&hw_resources);
[663f41c4]495 return EADDRNOTAVAIL;
[8c06905]496 }
[663f41c4]497 bus_data->conf_data_port = (char *) bus_data->conf_addr_port + 4;
[8c06905]498
499 dev->driver_data = bus_data;
500
[663f41c4]501 /* Enumerate child devices. */
[89ce401a]502 printf(NAME ": scanning the bus\n");
[5e598e0]503 pci_bus_scan(dev, 0);
[8c06905]504
505 clean_hw_resource_list(&hw_resources);
506
[df747b9c]507 return EOK;
[8c06905]508}
509
[663f41c4]510static void pciintel_init(void)
[3843ecb]511{
[5159ae9]512 pci_child_ops.interfaces[HW_RES_DEV_IFACE] = &pciintel_child_res_iface;
[3843ecb]513}
514
[8c06905]515int main(int argc, char *argv[])
516{
[3843ecb]517 printf(NAME ": HelenOS pci bus driver (intel method 1).\n");
518 pciintel_init();
[8c06905]519 return driver_main(&pci_driver);
520}
521
522/**
523 * @}
[472020fc]524 */
Note: See TracBrowser for help on using the repository browser.