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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 178673c was 178673c, checked in by Vojtech Horky <vojtechhorky@…>, 15 years ago

rootia32 driver works for amd64 as well

The driver was renamed to rootpc and is available for both
ia32 and amd64.

The PCI and NS8250 drivers were fixed to be compilable under amd64.

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