source: mainline/uspace/drv/pciintel/pci.c@ fb78ae72

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

Move interrupt enabling code to pciintel driver

  • Property mode set to 100644
File size: 15.7 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 <ipc/irc.h>
52#include <ipc/ns.h>
53#include <ipc/services.h>
54#include <sysinfo.h>
55#include <ops/hw_res.h>
56#include <device/hw_res.h>
57#include <ddi.h>
58#include <libarch/ddi.h>
59
60#include "pci.h"
61
62#define NAME "pciintel"
63
64#define CONF_ADDR(bus, dev, fn, reg) \
65 ((1 << 31) | (bus << 16) | (dev << 11) | (fn << 8) | (reg & ~3))
66
67static hw_resource_list_t *pciintel_get_child_resources(device_t *dev)
68{
69 pci_dev_data_t *dev_data = (pci_dev_data_t *) dev->driver_data;
70
71 if (dev_data == NULL)
72 return NULL;
73 return &dev_data->hw_resources;
74}
75
76static bool pciintel_enable_child_interrupt(device_t *dev)
77{
78 /* This is an old ugly way, copied from ne2000 driver */
79 assert(dev);
80 pci_dev_data_t *dev_data = (pci_dev_data_t *) dev->driver_data;
81
82 sysarg_t apic;
83 sysarg_t i8259;
84 int irc_phone = -1;
85 int irc_service = 0;
86
87 if ((sysinfo_get_value("apic", &apic) == EOK) && (apic)) {
88 irc_service = SERVICE_APIC;
89 } else if ((sysinfo_get_value("i8259", &i8259) == EOK) && (i8259)) {
90 irc_service = SERVICE_I8259;
91 }
92
93 if (irc_service) {
94 while (irc_phone < 0)
95 irc_phone = service_connect_blocking(irc_service, 0, 0);
96 } else {
97 return false;
98 }
99
100 size_t i;
101 for (i = 0; i < dev_data->hw_resources.count; i++) {
102 if (dev_data->hw_resources.resources[i].type == INTERRUPT) {
103 int irq = dev_data->hw_resources.resources[i].res.interrupt.irq;
104 async_msg_1(irc_phone, IRC_ENABLE_INTERRUPT, irq);
105 }
106 }
107
108 async_hangup(irc_phone);
109 return true;
110}
111
112static hw_res_ops_t pciintel_child_hw_res_ops = {
113 &pciintel_get_child_resources,
114 &pciintel_enable_child_interrupt
115};
116
117static device_ops_t pci_child_ops;
118
119static int pci_add_device(device_t *);
120
121/** The pci bus driver's standard operations. */
122static driver_ops_t pci_ops = {
123 .add_device = &pci_add_device
124};
125
126/** The pci bus driver structure. */
127static driver_t pci_driver = {
128 .name = NAME,
129 .driver_ops = &pci_ops
130};
131
132typedef struct pciintel_bus_data {
133 uint32_t conf_io_addr;
134 void *conf_data_port;
135 void *conf_addr_port;
136 fibril_mutex_t conf_mutex;
137} pci_bus_data_t;
138
139static pci_bus_data_t *create_pci_bus_data(void)
140{
141 pci_bus_data_t *bus_data;
142
143 bus_data = (pci_bus_data_t *) malloc(sizeof(pci_bus_data_t));
144 if (bus_data != NULL) {
145 memset(bus_data, 0, sizeof(pci_bus_data_t));
146 fibril_mutex_initialize(&bus_data->conf_mutex);
147 }
148
149 return bus_data;
150}
151
152static void delete_pci_bus_data(pci_bus_data_t *bus_data)
153{
154 free(bus_data);
155}
156
157static void pci_conf_read(device_t *dev, int reg, uint8_t *buf, size_t len)
158{
159 assert(dev->parent != NULL);
160
161 pci_dev_data_t *dev_data = (pci_dev_data_t *) dev->driver_data;
162 pci_bus_data_t *bus_data = (pci_bus_data_t *) dev->parent->driver_data;
163
164 fibril_mutex_lock(&bus_data->conf_mutex);
165
166 uint32_t conf_addr;
167 conf_addr = CONF_ADDR(dev_data->bus, dev_data->dev, dev_data->fn, reg);
168 void *addr = bus_data->conf_data_port + (reg & 3);
169
170 pio_write_32(bus_data->conf_addr_port, conf_addr);
171
172 switch (len) {
173 case 1:
174 buf[0] = pio_read_8(addr);
175 break;
176 case 2:
177 ((uint16_t *) buf)[0] = pio_read_16(addr);
178 break;
179 case 4:
180 ((uint32_t *) buf)[0] = pio_read_32(addr);
181 break;
182 }
183
184 fibril_mutex_unlock(&bus_data->conf_mutex);
185}
186
187static void pci_conf_write(device_t *dev, int reg, uint8_t *buf, size_t len)
188{
189 assert(dev->parent != NULL);
190
191 pci_dev_data_t *dev_data = (pci_dev_data_t *) dev->driver_data;
192 pci_bus_data_t *bus_data = (pci_bus_data_t *) dev->parent->driver_data;
193
194 fibril_mutex_lock(&bus_data->conf_mutex);
195
196 uint32_t conf_addr;
197 conf_addr = CONF_ADDR(dev_data->bus, dev_data->dev, dev_data->fn, reg);
198 void *addr = bus_data->conf_data_port + (reg & 3);
199
200 pio_write_32(bus_data->conf_addr_port, conf_addr);
201
202 switch (len) {
203 case 1:
204 pio_write_8(addr, buf[0]);
205 break;
206 case 2:
207 pio_write_16(addr, ((uint16_t *) buf)[0]);
208 break;
209 case 4:
210 pio_write_32(addr, ((uint32_t *) buf)[0]);
211 break;
212 }
213
214 fibril_mutex_unlock(&bus_data->conf_mutex);
215}
216
217uint8_t pci_conf_read_8(device_t *dev, int reg)
218{
219 uint8_t res;
220 pci_conf_read(dev, reg, &res, 1);
221 return res;
222}
223
224uint16_t pci_conf_read_16(device_t *dev, int reg)
225{
226 uint16_t res;
227 pci_conf_read(dev, reg, (uint8_t *) &res, 2);
228 return res;
229}
230
231uint32_t pci_conf_read_32(device_t *dev, int reg)
232{
233 uint32_t res;
234 pci_conf_read(dev, reg, (uint8_t *) &res, 4);
235 return res;
236}
237
238void pci_conf_write_8(device_t *dev, int reg, uint8_t val)
239{
240 pci_conf_write(dev, reg, (uint8_t *) &val, 1);
241}
242
243void pci_conf_write_16(device_t *dev, int reg, uint16_t val)
244{
245 pci_conf_write(dev, reg, (uint8_t *) &val, 2);
246}
247
248void pci_conf_write_32(device_t *dev, int reg, uint32_t val)
249{
250 pci_conf_write(dev, reg, (uint8_t *) &val, 4);
251}
252
253void create_pci_match_ids(device_t *dev)
254{
255 pci_dev_data_t *dev_data = (pci_dev_data_t *) dev->driver_data;
256 match_id_t *match_id = NULL;
257 char *match_id_str;
258
259 match_id = create_match_id();
260 if (match_id != NULL) {
261 asprintf(&match_id_str, "pci/ven=%04x&dev=%04x",
262 dev_data->vendor_id, dev_data->device_id);
263 match_id->id = match_id_str;
264 match_id->score = 90;
265 add_match_id(&dev->match_ids, match_id);
266 }
267
268 /* TODO add more ids (with subsys ids, using class id etc.) */
269}
270
271void
272pci_add_range(device_t *dev, uint64_t range_addr, size_t range_size, bool io)
273{
274 pci_dev_data_t *dev_data = (pci_dev_data_t *) dev->driver_data;
275 hw_resource_list_t *hw_res_list = &dev_data->hw_resources;
276 hw_resource_t *hw_resources = hw_res_list->resources;
277 size_t count = hw_res_list->count;
278
279 assert(hw_resources != NULL);
280 assert(count < PCI_MAX_HW_RES);
281
282 if (io) {
283 hw_resources[count].type = IO_RANGE;
284 hw_resources[count].res.io_range.address = range_addr;
285 hw_resources[count].res.io_range.size = range_size;
286 hw_resources[count].res.io_range.endianness = LITTLE_ENDIAN;
287 } else {
288 hw_resources[count].type = MEM_RANGE;
289 hw_resources[count].res.mem_range.address = range_addr;
290 hw_resources[count].res.mem_range.size = range_size;
291 hw_resources[count].res.mem_range.endianness = LITTLE_ENDIAN;
292 }
293
294 hw_res_list->count++;
295}
296
297/** Read the base address register (BAR) of the device and if it contains valid
298 * address add it to the devices hw resource list.
299 *
300 * @param dev The pci device.
301 * @param addr The address of the BAR in the PCI configuration address space of
302 * the device.
303 * @return The addr the address of the BAR which should be read next.
304 */
305int pci_read_bar(device_t *dev, int addr)
306{
307 /* Value of the BAR */
308 uint32_t val, mask;
309 /* IO space address */
310 bool io;
311 /* 64-bit wide address */
312 bool addrw64;
313
314 /* Size of the io or memory range specified by the BAR */
315 size_t range_size;
316 /* Beginning of the io or memory range specified by the BAR */
317 uint64_t range_addr;
318
319 /* Get the value of the BAR. */
320 val = pci_conf_read_32(dev, addr);
321
322 io = (bool) (val & 1);
323 if (io) {
324 addrw64 = false;
325 } else {
326 switch ((val >> 1) & 3) {
327 case 0:
328 addrw64 = false;
329 break;
330 case 2:
331 addrw64 = true;
332 break;
333 default:
334 /* reserved, go to the next BAR */
335 return addr + 4;
336 }
337 }
338
339 /* Get the address mask. */
340 pci_conf_write_32(dev, addr, 0xffffffff);
341 mask = pci_conf_read_32(dev, addr);
342
343 /* Restore the original value. */
344 pci_conf_write_32(dev, addr, val);
345 val = pci_conf_read_32(dev, addr);
346
347 range_size = pci_bar_mask_to_size(mask);
348
349 if (addrw64) {
350 range_addr = ((uint64_t)pci_conf_read_32(dev, addr + 4) << 32) |
351 (val & 0xfffffff0);
352 } else {
353 range_addr = (val & 0xfffffff0);
354 }
355
356 if (range_addr != 0) {
357 printf(NAME ": device %s : ", dev->name);
358 printf("address = %" PRIx64, range_addr);
359 printf(", size = %x\n", (unsigned int) range_size);
360 }
361
362 pci_add_range(dev, range_addr, range_size, io);
363
364 if (addrw64)
365 return addr + 8;
366
367 return addr + 4;
368}
369
370void pci_add_interrupt(device_t *dev, int irq)
371{
372 pci_dev_data_t *dev_data = (pci_dev_data_t *) dev->driver_data;
373 hw_resource_list_t *hw_res_list = &dev_data->hw_resources;
374 hw_resource_t *hw_resources = hw_res_list->resources;
375 size_t count = hw_res_list->count;
376
377 assert(NULL != hw_resources);
378 assert(count < PCI_MAX_HW_RES);
379
380 hw_resources[count].type = INTERRUPT;
381 hw_resources[count].res.interrupt.irq = irq;
382
383 hw_res_list->count++;
384
385 printf(NAME ": device %s uses irq %x.\n", dev->name, irq);
386}
387
388void pci_read_interrupt(device_t *dev)
389{
390 uint8_t irq = pci_conf_read_8(dev, PCI_BRIDGE_INT_LINE);
391 if (irq != 0xff)
392 pci_add_interrupt(dev, irq);
393}
394
395/** Enumerate (recursively) and register the devices connected to a pci bus.
396 *
397 * @param parent The host-to-pci bridge device.
398 * @param bus_num The bus number.
399 */
400void pci_bus_scan(device_t *parent, int bus_num)
401{
402 device_t *dev = create_device();
403 pci_dev_data_t *dev_data = create_pci_dev_data();
404 dev->driver_data = dev_data;
405 dev->parent = parent;
406
407 int child_bus = 0;
408 int dnum, fnum;
409 bool multi;
410 uint8_t header_type;
411
412 for (dnum = 0; dnum < 32; dnum++) {
413 multi = true;
414 for (fnum = 0; multi && fnum < 8; fnum++) {
415 init_pci_dev_data(dev_data, bus_num, dnum, fnum);
416 dev_data->vendor_id = pci_conf_read_16(dev,
417 PCI_VENDOR_ID);
418 dev_data->device_id = pci_conf_read_16(dev,
419 PCI_DEVICE_ID);
420 if (dev_data->vendor_id == 0xffff) {
421 /*
422 * The device is not present, go on scanning the
423 * bus.
424 */
425 if (fnum == 0)
426 break;
427 else
428 continue;
429 }
430
431 header_type = pci_conf_read_8(dev, PCI_HEADER_TYPE);
432 if (fnum == 0) {
433 /* Is the device multifunction? */
434 multi = header_type >> 7;
435 }
436 /* Clear the multifunction bit. */
437 header_type = header_type & 0x7F;
438
439 create_pci_dev_name(dev);
440
441 pci_alloc_resource_list(dev);
442 pci_read_bars(dev);
443 pci_read_interrupt(dev);
444
445 dev->ops = &pci_child_ops;
446
447 printf(NAME ": adding new child device %s.\n",
448 dev->name);
449
450 create_pci_match_ids(dev);
451
452 if (child_device_register(dev, parent) != EOK) {
453 pci_clean_resource_list(dev);
454 clean_match_ids(&dev->match_ids);
455 free((char *) dev->name);
456 dev->name = NULL;
457 continue;
458 }
459
460 if (header_type == PCI_HEADER_TYPE_BRIDGE ||
461 header_type == PCI_HEADER_TYPE_CARDBUS) {
462 child_bus = pci_conf_read_8(dev,
463 PCI_BRIDGE_SEC_BUS_NUM);
464 printf(NAME ": device is pci-to-pci bridge, "
465 "secondary bus number = %d.\n", bus_num);
466 if (child_bus > bus_num)
467 pci_bus_scan(parent, child_bus);
468 }
469
470 /* Alloc new aux. dev. structure. */
471 dev = create_device();
472 dev_data = create_pci_dev_data();
473 dev->driver_data = dev_data;
474 dev->parent = parent;
475 }
476 }
477
478 if (dev_data->vendor_id == 0xffff) {
479 delete_device(dev);
480 /* Free the auxiliary device structure. */
481 delete_pci_dev_data(dev_data);
482 }
483}
484
485static int pci_add_device(device_t *dev)
486{
487 int rc;
488
489 printf(NAME ": pci_add_device\n");
490
491 pci_bus_data_t *bus_data = create_pci_bus_data();
492 if (bus_data == NULL) {
493 printf(NAME ": pci_add_device allocation failed.\n");
494 return ENOMEM;
495 }
496
497 dev->parent_phone = devman_parent_device_connect(dev->handle,
498 IPC_FLAG_BLOCKING);
499 if (dev->parent_phone < 0) {
500 printf(NAME ": pci_add_device failed to connect to the "
501 "parent's driver.\n");
502 delete_pci_bus_data(bus_data);
503 return dev->parent_phone;
504 }
505
506 hw_resource_list_t hw_resources;
507
508 rc = hw_res_get_resource_list(dev->parent_phone, &hw_resources);
509 if (rc != EOK) {
510 printf(NAME ": pci_add_device failed to get hw resources for "
511 "the device.\n");
512 delete_pci_bus_data(bus_data);
513 async_hangup(dev->parent_phone);
514 return rc;
515 }
516
517 printf(NAME ": conf_addr = %" PRIx64 ".\n",
518 hw_resources.resources[0].res.io_range.address);
519
520 assert(hw_resources.count > 0);
521 assert(hw_resources.resources[0].type == IO_RANGE);
522 assert(hw_resources.resources[0].res.io_range.size == 8);
523
524 bus_data->conf_io_addr =
525 (uint32_t) hw_resources.resources[0].res.io_range.address;
526
527 if (pio_enable((void *)(uintptr_t)bus_data->conf_io_addr, 8,
528 &bus_data->conf_addr_port)) {
529 printf(NAME ": failed to enable configuration ports.\n");
530 delete_pci_bus_data(bus_data);
531 async_hangup(dev->parent_phone);
532 hw_res_clean_resource_list(&hw_resources);
533 return EADDRNOTAVAIL;
534 }
535 bus_data->conf_data_port = (char *) bus_data->conf_addr_port + 4;
536
537 dev->driver_data = bus_data;
538
539 /* Enumerate child devices. */
540 printf(NAME ": scanning the bus\n");
541 pci_bus_scan(dev, 0);
542
543 hw_res_clean_resource_list(&hw_resources);
544
545 return EOK;
546}
547
548static void pciintel_init(void)
549{
550 pci_child_ops.interfaces[HW_RES_DEV_IFACE] = &pciintel_child_hw_res_ops;
551}
552
553pci_dev_data_t *create_pci_dev_data(void)
554{
555 pci_dev_data_t *res = (pci_dev_data_t *) malloc(sizeof(pci_dev_data_t));
556
557 if (res != NULL)
558 memset(res, 0, sizeof(pci_dev_data_t));
559 return res;
560}
561
562void init_pci_dev_data(pci_dev_data_t *dev_data, int bus, int dev, int fn)
563{
564 dev_data->bus = bus;
565 dev_data->dev = dev;
566 dev_data->fn = fn;
567}
568
569void delete_pci_dev_data(pci_dev_data_t *dev_data)
570{
571 if (dev_data != NULL) {
572 hw_res_clean_resource_list(&dev_data->hw_resources);
573 free(dev_data);
574 }
575}
576
577void create_pci_dev_name(device_t *dev)
578{
579 pci_dev_data_t *dev_data = (pci_dev_data_t *) dev->driver_data;
580 char *name = NULL;
581
582 asprintf(&name, "%02x:%02x.%01x", dev_data->bus, dev_data->dev,
583 dev_data->fn);
584 dev->name = name;
585}
586
587bool pci_alloc_resource_list(device_t *dev)
588{
589 pci_dev_data_t *dev_data = (pci_dev_data_t *)dev->driver_data;
590
591 dev_data->hw_resources.resources =
592 (hw_resource_t *) malloc(PCI_MAX_HW_RES * sizeof(hw_resource_t));
593 return dev_data->hw_resources.resources != NULL;
594}
595
596void pci_clean_resource_list(device_t *dev)
597{
598 pci_dev_data_t *dev_data = (pci_dev_data_t *) dev->driver_data;
599
600 if (dev_data->hw_resources.resources != NULL) {
601 free(dev_data->hw_resources.resources);
602 dev_data->hw_resources.resources = NULL;
603 }
604}
605
606/** Read the base address registers (BARs) of the device and adds the addresses
607 * to its hw resource list.
608 *
609 * @param dev the pci device.
610 */
611void pci_read_bars(device_t *dev)
612{
613 /*
614 * Position of the BAR in the PCI configuration address space of the
615 * device.
616 */
617 int addr = PCI_BASE_ADDR_0;
618
619 while (addr <= PCI_BASE_ADDR_5)
620 addr = pci_read_bar(dev, addr);
621}
622
623size_t pci_bar_mask_to_size(uint32_t mask)
624{
625 return ((mask & 0xfffffff0) ^ 0xffffffff) + 1;
626}
627
628int main(int argc, char *argv[])
629{
630 printf(NAME ": HelenOS pci bus driver (intel method 1).\n");
631 pciintel_init();
632 return driver_main(&pci_driver);
633}
634
635/**
636 * @}
637 */
Note: See TracBrowser for help on using the repository browser.