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
Line 
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>
43#include <str.h>
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>
54#include <libarch/ddi.h>
55
56#include "pci.h"
57
58#define NAME "pciintel"
59
60#define CONF_ADDR(bus, dev, fn, reg) \
61 ((1 << 31) | (bus << 16) | (dev << 11) | (fn << 8) | (reg & ~3))
62
63static hw_resource_list_t *pciintel_get_child_resources(device_t *dev)
64{
65 pci_dev_data_t *dev_data = (pci_dev_data_t *) dev->driver_data;
66
67 if (NULL == dev_data)
68 return NULL;
69 return &dev_data->hw_resources;
70}
71
72static bool pciintel_enable_child_interrupt(device_t *dev)
73{
74 /* TODO */
75
76 return false;
77}
78
79static resource_iface_t pciintel_child_res_iface = {
80 &pciintel_get_child_resources,
81 &pciintel_enable_child_interrupt
82};
83
84static device_ops_t pci_child_ops;
85
86static int pci_add_device(device_t *);
87
88/** The pci bus driver's standard operations. */
89static driver_ops_t pci_ops = {
90 .add_device = &pci_add_device
91};
92
93/** The pci bus driver structure. */
94static driver_t pci_driver = {
95 .name = NAME,
96 .driver_ops = &pci_ops
97};
98
99typedef struct pciintel_bus_data {
100 uint32_t conf_io_addr;
101 void *conf_data_port;
102 void *conf_addr_port;
103 fibril_mutex_t conf_mutex;
104} pci_bus_data_t;
105
106static pci_bus_data_t *create_pci_bus_data(void)
107{
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) {
112 memset(bus_data, 0, sizeof(pci_bus_data_t));
113 fibril_mutex_initialize(&bus_data->conf_mutex);
114 }
115 return bus_data;
116}
117
118static void delete_pci_bus_data(pci_bus_data_t *bus_data)
119{
120 free(bus_data);
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
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;
129
130 fibril_mutex_lock(&bus_data->conf_mutex);
131
132 uint32_t conf_addr;
133 conf_addr = CONF_ADDR(dev_data->bus, dev_data->dev, dev_data->fn, reg);
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) {
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;
148 }
149
150 fibril_mutex_unlock(&bus_data->conf_mutex);
151}
152
153static void pci_conf_write(device_t *dev, int reg, uint8_t *buf, size_t len)
154{
155 assert(NULL != dev->parent);
156
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;
159
160 fibril_mutex_lock(&bus_data->conf_mutex);
161
162 uint32_t conf_addr;
163 conf_addr = CONF_ADDR(dev_data->bus, dev_data->dev, dev_data->fn, reg);
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) {
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;
178 }
179
180 fibril_mutex_unlock(&bus_data->conf_mutex);
181}
182
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;
193 pci_conf_read(dev, reg, (uint8_t *) &res, 2);
194 return res;
195}
196
197uint32_t pci_conf_read_32(device_t *dev, int reg)
198{
199 uint32_t res;
200 pci_conf_read(dev, reg, (uint8_t *) &res, 4);
201 return res;
202}
203
204void pci_conf_write_8(device_t *dev, int reg, uint8_t val)
205{
206 pci_conf_write(dev, reg, (uint8_t *) &val, 1);
207}
208
209void pci_conf_write_16(device_t *dev, int reg, uint16_t val)
210{
211 pci_conf_write(dev, reg, (uint8_t *) &val, 2);
212}
213
214void pci_conf_write_32(device_t *dev, int reg, uint32_t val)
215{
216 pci_conf_write(dev, reg, (uint8_t *) &val, 4);
217}
218
219void create_pci_match_ids(device_t *dev)
220{
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
225 match_id = create_match_id();
226 if (NULL != match_id) {
227 asprintf(&match_id_str, "pci/ven=%04x&dev=%04x",
228 dev_data->vendor_id, dev_data->device_id);
229 match_id->id = match_id_str;
230 match_id->score = 90;
231 add_match_id(&dev->match_ids, match_id);
232 }
233 /* TODO add more ids (with subsys ids, using class id etc.) */
234}
235
236void
237pci_add_range(device_t *dev, uint64_t range_addr, size_t range_size, bool io)
238{
239 pci_dev_data_t *dev_data = (pci_dev_data_t *) dev->driver_data;
240 hw_resource_list_t *hw_res_list = &dev_data->hw_resources;
241 hw_resource_t *hw_resources = hw_res_list->resources;
242 size_t count = hw_res_list->count;
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;
250 hw_resources[count].res.io_range.size = range_size;
251 hw_resources[count].res.io_range.endianness = LITTLE_ENDIAN;
252 } else {
253 hw_resources[count].type = MEM_RANGE;
254 hw_resources[count].res.mem_range.address = range_addr;
255 hw_resources[count].res.mem_range.size = range_size;
256 hw_resources[count].res.mem_range.endianness = LITTLE_ENDIAN;
257 }
258
259 hw_res_list->count++;
260}
261
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.
269 */
270int pci_read_bar(device_t *dev, int addr)
271{
272 /* Value of the BAR */
273 uint32_t val, mask;
274 /* IO space address */
275 bool io;
276 /* 64-bit wide address */
277 bool w64;
278
279 /* Size of the io or memory range specified by the BAR */
280 size_t range_size;
281 /* Beginning of the io or memory range specified by the BAR */
282 uint64_t range_addr;
283
284 /* Get the value of the BAR. */
285 val = pci_conf_read_32(dev, addr);
286
287 io = (bool) (val & 1);
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:
299 /* reserved, go to the next BAR */
300 return addr + 4;
301 }
302 }
303
304 /* Get the address mask. */
305 pci_conf_write_32(dev, addr, 0xffffffff);
306 mask = pci_conf_read_32(dev, addr);
307
308 /* Restore the original value. */
309 pci_conf_write_32(dev, addr, val);
310 val = pci_conf_read_32(dev, addr);
311
312 range_size = pci_bar_mask_to_size(mask);
313
314 if (w64) {
315 range_addr = ((uint64_t)pci_conf_read_32(dev, addr + 4) << 32) |
316 (val & 0xfffffff0);
317 } else {
318 range_addr = (val & 0xfffffff0);
319 }
320
321 if (0 != range_addr) {
322 printf(NAME ": device %s : ", dev->name);
323 printf("address = %x", range_addr);
324 printf(", size = %x\n", range_size);
325 }
326
327 pci_add_range(dev, range_addr, range_size, io);
328
329 if (w64)
330 return addr + 8;
331
332 return addr + 4;
333}
334
335void pci_add_interrupt(device_t *dev, int irq)
336{
337 pci_dev_data_t *dev_data = (pci_dev_data_t *) dev->driver_data;
338 hw_resource_list_t *hw_res_list = &dev_data->hw_resources;
339 hw_resource_t *hw_resources = hw_res_list->resources;
340 size_t count = hw_res_list->count;
341
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
348 hw_res_list->count++;
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);
356 if (0xff != irq)
357 pci_add_interrupt(dev, irq);
358}
359
360/** Enumerate (recursively) and register the devices connected to a pci bus.
361 *
362 * @param parent The host-to-pci bridge device.
363 * @param bus_num The bus number.
364 */
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;
375 uint8_t header_type;
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);
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)
391 break;
392 else
393 continue;
394 }
395
396 header_type = pci_conf_read_8(dev, PCI_HEADER_TYPE);
397 if (fnum == 0) {
398 /* Is the device multifunction? */
399 multi = header_type >> 7;
400 }
401 /* Clear the multifunction bit. */
402 header_type = header_type & 0x7F;
403
404 create_pci_dev_name(dev);
405
406 pci_alloc_resource_list(dev);
407 pci_read_bars(dev);
408 pci_read_interrupt(dev);
409
410 dev->ops = &pci_child_ops;
411
412 printf(NAME ": adding new child device %s.\n",
413 dev->name);
414
415 create_pci_match_ids(dev);
416
417 if (EOK != child_device_register(dev, parent)) {
418 pci_clean_resource_list(dev);
419 clean_match_ids(&dev->match_ids);
420 free((char *) dev->name);
421 dev->name = NULL;
422 continue;
423 }
424
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);
433 }
434
435 /* Alloc new aux. dev. structure. */
436 dev = create_device();
437 dev_data = create_pci_dev_data();
438 dev->driver_data = dev_data;
439 dev->parent = parent;
440 }
441 }
442
443 if (dev_data->vendor_id == 0xffff) {
444 delete_device(dev);
445 /* Free the auxiliary device structure. */
446 delete_pci_dev_data(dev_data);
447 }
448}
449
450static int pci_add_device(device_t *dev)
451{
452 printf(NAME ": pci_add_device\n");
453
454 pci_bus_data_t *bus_data = create_pci_bus_data();
455 if (NULL == bus_data) {
456 printf(NAME ": pci_add_device allocation failed.\n");
457 return ENOMEM;
458 }
459
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");
465 delete_pci_bus_data(bus_data);
466 return EPARTY; /* FIXME: use another EC */
467 }
468
469 hw_resource_list_t hw_resources;
470
471 if (!get_hw_resources(dev->parent_phone, &hw_resources)) {
472 printf(NAME ": pci_add_device failed to get hw resources for "
473 "the device.\n");
474 delete_pci_bus_data(bus_data);
475 ipc_hangup(dev->parent_phone);
476 return EPARTY; /* FIXME: use another EC */
477 }
478
479 printf(NAME ": conf_addr = %x.\n",
480 hw_resources.resources[0].res.io_range.address);
481
482 assert(hw_resources.count > 0);
483 assert(hw_resources.resources[0].type == IO_RANGE);
484 assert(hw_resources.resources[0].res.io_range.size == 8);
485
486 bus_data->conf_io_addr =
487 (uint32_t) hw_resources.resources[0].res.io_range.address;
488
489 if (pio_enable((void *)bus_data->conf_io_addr, 8,
490 &bus_data->conf_addr_port)) {
491 printf(NAME ": failed to enable configuration ports.\n");
492 delete_pci_bus_data(bus_data);
493 ipc_hangup(dev->parent_phone);
494 clean_hw_resource_list(&hw_resources);
495 return EADDRNOTAVAIL;
496 }
497 bus_data->conf_data_port = (char *) bus_data->conf_addr_port + 4;
498
499 dev->driver_data = bus_data;
500
501 /* Enumerate child devices. */
502 printf(NAME ": scanning the bus\n");
503 pci_bus_scan(dev, 0);
504
505 clean_hw_resource_list(&hw_resources);
506
507 return EOK;
508}
509
510static void pciintel_init(void)
511{
512 pci_child_ops.interfaces[HW_RES_DEV_IFACE] = &pciintel_child_res_iface;
513}
514
515int main(int argc, char *argv[])
516{
517 printf(NAME ": HelenOS pci bus driver (intel method 1).\n");
518 pciintel_init();
519 return driver_main(&pci_driver);
520}
521
522/**
523 * @}
524 */
Note: See TracBrowser for help on using the repository browser.