source: mainline/uspace/srv/drivers/pciintel/pci.c@ d1fc8f0

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since d1fc8f0 was d1fc8f0, checked in by Lenka Trochtova <trochtova.lenka@…>, 15 years ago

pci base address registers reading - parts of code

  • Property mode set to 100644
File size: 11.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 <string.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#include "pci_regs.h"
58
59#define NAME "pciintel"
60
61
62#define CONF_ADDR(bus, dev, fn, reg) ((1 << 31) | (bus << 16) | (dev << 11) | (fn << 8) | (reg & ~3))
63
64
65static bool pci_add_device(device_t *dev);
66
67/** The pci bus driver's standard operations.
68 */
69static driver_ops_t pci_ops = {
70 .add_device = &pci_add_device
71};
72
73/** The pci bus driver structure.
74 */
75static driver_t pci_driver = {
76 .name = NAME,
77 .driver_ops = &pci_ops
78};
79
80typedef struct pciintel_bus_data {
81 uint32_t conf_io_addr;
82 void *conf_data_port;
83 void *conf_addr_port;
84 fibril_mutex_t conf_mutex;
85} pci_bus_data_t;
86
87static inline pci_bus_data_t *create_pci_bus_data()
88{
89 pci_bus_data_t *bus_data = (pci_bus_data_t *)malloc(sizeof(pci_bus_data_t));
90 if(NULL != bus_data) {
91 memset(bus_data, 0, sizeof(pci_bus_data_t));
92 fibril_mutex_initialize(&bus_data->conf_mutex);
93 }
94 return bus_data;
95}
96
97static inline void delete_pci_bus_data(pci_bus_data_t *bus_data)
98{
99 free(bus_data);
100}
101
102static void pci_conf_read(device_t *dev, int reg, uint8_t *buf, size_t len)
103{
104 assert(NULL != dev->parent);
105
106 pci_dev_data_t *dev_data = (pci_dev_data_t *)dev->driver_data;
107 pci_bus_data_t *bus_data = (pci_bus_data_t *)dev->parent->driver_data;
108
109 fibril_mutex_lock(&bus_data->conf_mutex);
110
111 uint32_t conf_addr = CONF_ADDR(dev_data->bus, dev_data->dev, dev_data->fn, reg);
112 void *addr = bus_data->conf_data_port + (reg & 3);
113
114 pio_write_32(bus_data->conf_addr_port, conf_addr);
115
116 switch (len) {
117 case 1:
118 buf[0] = pio_read_8(addr);
119 break;
120 case 2:
121 ((uint16_t *)buf)[0] = pio_read_16(addr);
122 break;
123 case 4:
124 ((uint32_t *)buf)[0] = pio_read_32(addr);
125 break;
126 }
127
128 fibril_mutex_unlock(&bus_data->conf_mutex);
129}
130
131static void pci_conf_write(device_t *dev, int reg, uint8_t *buf, size_t len)
132{
133 assert(NULL != dev->parent);
134
135 pci_dev_data_t *dev_data = (pci_dev_data_t *)dev->driver_data;
136 pci_bus_data_t *bus_data = (pci_bus_data_t *)dev->parent->driver_data;
137
138 fibril_mutex_lock(&bus_data->conf_mutex);
139
140 uint32_t conf_addr = CONF_ADDR(dev_data->bus, dev_data->dev, dev_data->fn, reg);
141 void *addr = bus_data->conf_data_port + (reg & 3);
142
143 pio_write_32(bus_data->conf_addr_port, conf_addr);
144
145 switch (len) {
146 case 1:
147 pio_write_8(addr, buf[0]);
148 break;
149 case 2:
150 pio_write_16(addr, ((uint16_t *)buf)[0]);
151 break;
152 case 4:
153 pio_write_32(addr, ((uint32_t *)buf)[0]);
154 break;
155 }
156
157 fibril_mutex_unlock(&bus_data->conf_mutex);
158}
159
160uint8_t pci_conf_read_8(device_t *dev, int reg)
161{
162 uint8_t res;
163 pci_conf_read(dev, reg, &res, 1);
164 return res;
165}
166
167uint16_t pci_conf_read_16(device_t *dev, int reg)
168{
169 uint16_t res;
170 pci_conf_read(dev, reg, (uint8_t *)&res, 2);
171 return res;
172}
173
174uint32_t pci_conf_read_32(device_t *dev, int reg)
175{
176 uint32_t res;
177 pci_conf_read(dev, reg, (uint8_t *)&res, 4);
178 return res;
179}
180
181void pci_conf_write_8(device_t *dev, int reg, uint8_t val)
182{
183 pci_conf_write(dev, reg, (uint8_t *)&val, 4);
184}
185
186void pci_conf_write_16(device_t *dev, int reg, uint16_t val)
187{
188 pci_conf_write(dev, reg, (uint8_t *)&val, 4);
189}
190
191void pci_conf_write_32(device_t *dev, int reg, uint32_t val)
192{
193 pci_conf_write(dev, reg, (uint8_t *)&val, 4);
194}
195
196
197void create_pci_match_ids(device_t *dev)
198{
199 pci_dev_data_t *dev_data = (pci_dev_data_t *)dev->driver_data;
200 match_id_t *match_id = NULL;
201 match_id = create_match_id();
202 if (NULL != match_id) {
203 asprintf(&match_id->id, "pci/ven=%04x&dev=%04x", dev_data->vendor_id, dev_data->device_id);
204 match_id->score = 90;
205 add_match_id(&dev->match_ids, match_id);
206 }
207 // TODO add more ids (with subsys ids, using class id etc.)
208}
209
210
211static size_t range_size_form_mask(uint32_t mask)
212{
213 // TODO
214 return 0;
215}
216
217/** Read the base address register (BAR) of the device
218 * and if it contains valid address add it to the devices hw resource list.
219 *
220 * @param dev the pci device.
221 * @param addr the address of the BAR in the PCI configuration address space of the device.
222 *
223 * @return the addr the address of the BAR which should be read next.
224 */
225static int pci_read_bar(device_t *dev, int addr)
226{
227 // value of the BAR
228 uint32_t val, mask;
229 // IO space address
230 bool io;
231 // 64-bit wide address
232 bool w64;
233
234 // size of the io or memory range specified by the BAR
235 size_t range_size;
236 // beginning of the io or memory range specified by the BAR
237 uint64_t range_addr;
238
239
240 val = pci_conf_read_32(dev, addr);
241
242 io = (bool)(val & 1);
243 if (io) {
244 w64 = false;
245 } else {
246 switch ((val >> 1) & 3) {
247 case 0:
248 w64 = false;
249 break;
250 case 2:
251 w64 = true;
252 break;
253 default:
254 // reserved, go to the next BAR
255 return addr + 4;
256 }
257 }
258
259 // get the address mask
260 pci_conf_write_32(dev, addr, 0xffffffff);
261 mask = pci_conf_read_32(dev, addr);
262
263 // restore the original value
264 pci_conf_write_32(dev, addr, val);
265
266 range_size = range_size_form_mask(mask);
267
268 if (w64) {
269 range_addr = ((uint64_t)pci_conf_read_32(dev, addr + 4) << 32) | (val & 0xfffffff0);
270 } else {
271 range_addr = (val & 0xfffffff0);
272 }
273 if (0 != range_addr) {
274 printf(NAME ": device address = %x\n", range_addr);
275 }
276
277 // TODO add to the HW resource list
278
279 if (w64) {
280 return addr + 8;
281 }
282 return addr + 4;
283}
284
285/** Read the base address registers (BARs) of the device
286 * and adds the addresses to its hw resource list.
287 *
288 * @param dev the pci device.
289 */
290static void pci_read_bars(device_t *dev)
291{
292 // position of the BAR in the PCI configuration address space of the device
293 int addr = PCI_BASE_ADDR_0;
294
295 while (addr <= PCI_BASE_ADDR_5) {
296 addr = pci_read_bar(dev, addr);
297 }
298}
299
300/** Enumerate (recursively) and register the devices connected to a pci bus.
301 *
302 * @param parent the host-to-pci bridge device.
303 * @param bus_num the bus number.
304 */
305void pci_bus_scan(device_t *parent, int bus_num)
306{
307 device_t *dev = create_device();
308 pci_dev_data_t *dev_data = create_pci_dev_data();
309 dev->driver_data = dev_data;
310 dev->parent = parent;
311
312 int child_bus = 0;
313 int dnum, fnum;
314 bool multi;
315 uint8_t header_type;
316
317 for (dnum = 0; dnum < 32; dnum++) {
318 multi = true;
319 for (fnum = 0; multi && fnum < 8; fnum++) {
320 init_pci_dev_data(dev_data, bus_num, dnum, fnum);
321 dev_data->vendor_id = pci_conf_read_16(dev, PCI_VENDOR_ID);
322 dev_data->device_id = pci_conf_read_16(dev, PCI_DEVICE_ID);
323 if (dev_data->vendor_id == 0xFFFF) { // device is not present, go on scanning the bus
324 if (fnum == 0) {
325 break;
326 } else {
327 continue;
328 }
329 }
330 header_type = pci_conf_read_8(dev, PCI_HEADER_TYPE);
331 if (fnum == 0) {
332 multi = header_type >> 7; // is the device multifunction?
333 }
334 header_type = header_type & 0x7F; // clear the multifunction bit
335
336 // TODO initialize device - interfaces, hw resources
337 pci_read_bars(dev);
338
339 create_pci_dev_name(dev);
340 printf(NAME ": adding new child device %s.\n", dev->name);
341
342 create_pci_match_ids(dev);
343
344 if (!child_device_register(dev, parent)) {
345 clean_match_ids(&dev->match_ids);
346 free((char *)dev->name);
347 dev->name = NULL;
348 continue;
349 }
350
351 //printf(NAME ": new device %s was successfully registered by device manager.\n", dev->name);
352
353 if (header_type == PCI_HEADER_TYPE_BRIDGE || header_type == PCI_HEADER_TYPE_CARDBUS ) {
354 child_bus = pci_conf_read_8(dev, PCI_BRIDGE_SEC_BUS_NUM);
355 printf(NAME ": device is pci-to-pci bridge, secondary bus number = %d.\n", bus_num);
356 if(child_bus > bus_num) {
357 pci_bus_scan(parent, child_bus);
358 }
359 }
360
361 dev = create_device(); // alloc new aux. dev. structure
362 dev_data = create_pci_dev_data();
363 dev->driver_data = dev_data;
364 dev->parent = parent;
365 }
366 }
367
368 if (dev_data->vendor_id == 0xFFFF) {
369 delete_device(dev);
370 delete_pci_dev_data(dev_data); // free the auxiliary device structure
371 }
372
373}
374
375static bool pci_add_device(device_t *dev)
376{
377 printf(NAME ": pci_add_device\n");
378
379 pci_bus_data_t *bus_data = create_pci_bus_data();
380 if (NULL == bus_data) {
381 printf(NAME ": pci_add_device allocation failed.\n");
382 return false;
383 }
384
385 dev->parent_phone = devman_parent_device_connect(dev->handle, IPC_FLAG_BLOCKING);
386 if (dev->parent_phone <= 0) {
387 printf(NAME ": pci_add_device failed to connect to the parent's driver.\n");
388 delete_pci_bus_data(bus_data);
389 return false;
390 }
391
392 hw_resource_list_t hw_resources;
393
394 if (!get_hw_resources(dev->parent_phone, &hw_resources)) {
395 printf(NAME ": pci_add_device failed to get hw resources for the device.\n");
396 delete_pci_bus_data(bus_data);
397 ipc_hangup(dev->parent_phone);
398 return false;
399 }
400
401
402 printf(NAME ": conf_addr = %x.\n", hw_resources.resources[0].res.reg.address);
403
404 assert(hw_resources.count > 0);
405 assert(hw_resources.resources[0].type == REGISTER);
406 assert(hw_resources.resources[0].res.reg.size == 8);
407
408 bus_data->conf_io_addr = (uint32_t)hw_resources.resources[0].res.reg.address;
409
410 if (pio_enable((void *)bus_data->conf_io_addr, 8, &bus_data->conf_addr_port)) {
411 printf(NAME ": failed to enable configuration ports.\n");
412 delete_pci_bus_data(bus_data);
413 ipc_hangup(dev->parent_phone);
414 clean_hw_resource_list(&hw_resources);
415 return false;
416 }
417 bus_data->conf_data_port = (char *)bus_data->conf_addr_port + 4;
418
419 dev->driver_data = bus_data;
420
421 // enumerate child devices
422 printf(NAME ": scanning the bus\n");
423 pci_bus_scan(dev, 0);
424
425 clean_hw_resource_list(&hw_resources);
426
427 return true;
428}
429
430int main(int argc, char *argv[])
431{
432 printf(NAME ": HelenOS pci bus driver (intel method 1).\n");
433 return driver_main(&pci_driver);
434}
435
436/**
437 * @}
438 */
Note: See TracBrowser for help on using the repository browser.