cynix

x86 UNIX-like OS
git clone git://git.2f30.org/cynix
Log | Files | Refs | README | LICENSE

pci.c (6029B)


      1 /*
      2  *  core/pci.c
      3  *
      4  *  Copyright (C) 2010 stateless
      5  */
      6 
      7 #include <pci.h>
      8 #include <x86.h>
      9 #include <string.h>
     10 #include <heap.h>
     11 #include <errno.h>
     12 #include <serial.h>
     13 
     14 static int has_pci_init = 0;
     15 static struct list_head rootbus;
     16 static spinlock_t pci_lock;
     17 
     18 uint32_t
     19 pci_read32(struct pci_config_addr_t *addr)
     20 {
     21 	uint32_t r;
     22 
     23 	acquire(&pci_lock);
     24 	if (addr->reg_num & 0x3)
     25 		panic("offset is not 0-padded!");
     26 	outl(CONFIG_ADDRESS, *(uint32_t *)addr);
     27 	r = inl(CONFIG_DATA);
     28 	release(&pci_lock);
     29 	return r;
     30 }
     31 
     32 uint32_t
     33 pci_init(void)
     34 {
     35 	uint32_t ret, tmp;
     36 
     37 	initspinlock(&pci_lock);
     38 	outl(CONFIG_ADDRESS, 0);
     39 	outl(0xcfa, 0);
     40 	if (inl(CONFIG_ADDRESS) == 0 && inl(0xcfa) == 0) {
     41 		info("PCI type-2 is not supported!\n");
     42 		return 1;
     43 	}
     44 	tmp = inl(CONFIG_ADDRESS);
     45 	outl(CONFIG_ADDRESS, 0x80000000);
     46 	ret = inl(CONFIG_ADDRESS);
     47 	outl(CONFIG_ADDRESS, tmp);
     48 	if (ret == 0x80000000)
     49 		has_pci_init = 1;
     50 	return ret != 0x80000000;
     51 }
     52 
     53 static void
     54 dump_dev(struct pci_dev_t *dev)
     55 {
     56 	struct pci_config_addr_t addr;
     57 	union pci_config_space_t *cspace;
     58 	uint32_t reg = 0x10, r;
     59 
     60 	printf("Bus = %d, Device = %d\n", dev->busn, dev->devn);
     61 	cspace = dev->cspace;
     62 	if (cspace->type0.header_type == 0x1)
     63 		printf("\tPCI-to-PCI bridge\n");
     64 	printf("\tvendor_id = 0x%04lx, device_id = 0x%04lx\n",
     65 	       cspace->type0.vendor_id, cspace->type0.device_id);
     66 	printf("\tstatus = 0x%04lx, command = 0x%04lx\n",
     67 	       cspace->type0.status, cspace->type0.command);
     68 	printf("\tclass = 0x%02lx, subclass = 0x%02lx\n",
     69 	       cspace->type0.class, cspace->type0.subclass);
     70 	printf("\tIRQ = %u\n", cspace->type0.irq);
     71 
     72 	serial_dump("Bus = %d, Device = %d\n", dev->busn, dev->devn);
     73 	cspace = dev->cspace;
     74 	if (cspace->type0.header_type == 0x1)
     75 		serial_dump("\tPCI-to-PCI bridge\n");
     76 	serial_dump("\tvendor_id = 0x%04lx, device_id = 0x%04lx\n",
     77 		    cspace->type0.vendor_id, cspace->type0.device_id);
     78 	serial_dump("\tstatus = 0x%04lx, command = 0x%04lx\n",
     79 		    cspace->type0.status, cspace->type0.command);
     80 	serial_dump("\tclass = 0x%02lx, subclass = 0x%02lx\n",
     81 		    cspace->type0.class, cspace->type0.subclass);
     82 	serial_dump("\tIRQ = %u\n", cspace->type0.irq);
     83 
     84 	addr.enable_bit = 1;
     85 	addr.bus_num = dev->busn;
     86 	addr.dev_num = dev->devn;
     87 	if (cspace->type0.header_type) {
     88 		if (cspace->type0.header_type & 0x80)
     89 			printf("\tmultiple functions are available\n");
     90 		addr.reg_num = 0x18;
     91 		r = pci_read32(&addr);
     92 		printf("\tprimary bus = %u, secondary bus = %u\n",
     93 		       r & 0xff, (r >> 8) & 0xff);
     94 	}
     95 	for (; reg <= (cspace->type0.header_type == 0x1 ? 0x14 : 0x24);
     96 			reg += 4) {
     97 		addr.reg_num = reg;
     98 		r = pci_read32(&addr);
     99 		if (r != 0 && r != 0xffffffff)
    100 			printf("\tbar%d = 0x%08lx\n",
    101 			       (reg - 0x10) / 4, r);
    102 	}
    103 }
    104 
    105 void
    106 lspci(void)
    107 {
    108 	struct list_head *iter_bus, *iter_dev;
    109 	struct pci_dev_t *dev;
    110 	struct pci_bus_t *bus;
    111 
    112 	if (!has_pci_init) {
    113 		info("there is no PCI bus!\n");
    114 		return;
    115 	}
    116 
    117 	list_for_each(iter_bus, &rootbus) {
    118 		bus = list_entry(iter_bus, struct pci_bus_t, pci_bus_list);
    119 		list_for_each(iter_dev, &bus->pci_dev_list) {
    120 			dev = list_entry(iter_dev, struct pci_dev_t, list);
    121 			dump_dev(dev);
    122 		}
    123 	}
    124 }
    125 
    126 static int
    127 load_config_space(struct pci_dev_t *dev, struct pci_config_addr_t addr)
    128 {
    129 	uint32_t r, reg, *p;
    130 	union pci_config_space_t *cspace;
    131 
    132 	cspace = kmalloc(sizeof(*cspace));
    133 	if (IS_ERR(cspace))
    134 		return PTR_ERR(cspace);
    135 	memset(cspace, 0, sizeof(cspace));
    136 	p = (uint32_t *)cspace;
    137 	addr.enable_bit = 1;
    138 	for (reg = 0; reg <= 0x3c; reg += 4) {
    139 		addr.reg_num = reg;
    140 		r = pci_read32(&addr);
    141 		if (r != 0)
    142 			*p = r;
    143 		++p;
    144 	}
    145 	dev->cspace = cspace;
    146 	return 0;
    147 }
    148 
    149 static void
    150 cleanup(void)
    151 {
    152 	struct list_head *iter_bus, *iter_dev, *tmp1, *tmp2;
    153 	struct pci_dev_t *dev;
    154 	struct pci_bus_t *bus;
    155 
    156 	list_for_each_safe(iter_bus, tmp1, &rootbus) {
    157 		bus = list_entry(iter_bus, struct pci_bus_t, pci_bus_list);
    158 		list_for_each_safe(iter_dev, tmp2, &bus->pci_dev_list) {
    159 			dev = list_entry(iter_dev, struct pci_dev_t, list);
    160 			list_del(iter_dev);
    161 			kfree(dev->cspace);
    162 			kfree(dev);
    163 		}
    164 		list_del(iter_bus);
    165 		kfree(bus);
    166 	}
    167 }
    168 
    169 static struct pci_bus_t *
    170 create_bus(void) {
    171 	struct pci_bus_t *bus;
    172 
    173 	bus = kmalloc(sizeof(*bus));
    174 	if (IS_ERR(bus)) {
    175 		cleanup();
    176 		return bus;
    177 	}
    178 	INIT_LIST_HEAD(&bus->pci_dev_list);
    179 	return bus;
    180 }
    181 
    182 static int
    183 attach_dev(struct pci_bus_t *bus, struct pci_config_addr_t addr,
    184 	   int busn, int devn)
    185 {
    186 	int ret;
    187 	struct pci_dev_t *dev;
    188 
    189 	dev = kmalloc(sizeof(*dev));
    190 	if (IS_ERR(dev)) {
    191 		cleanup();
    192 		return PTR_ERR(dev);
    193 	}
    194 	memset(dev, 0, sizeof(*dev));
    195 	dev->cspace = NULL;
    196 	dev->bus = bus;
    197 	dev->busn = busn;
    198 	dev->devn = devn;
    199 	ret = load_config_space(dev, addr);
    200 	if (ret < 0) {
    201 		assert(IS_ERR(dev->cspace));
    202 		kfree(dev);
    203 		cleanup();
    204 		return -1;
    205 	}
    206 	list_add_tail(&dev->list, &bus->pci_dev_list);
    207 	return 0;
    208 }
    209 
    210 int
    211 pci_enumerate(void)
    212 {
    213 	struct pci_bus_t *busp;
    214 	struct pci_config_addr_t addr;
    215 	int dev, bus, ret, f;
    216 	uint32_t r;
    217 
    218 	INIT_LIST_HEAD(&rootbus);
    219 	memset(&addr, 0, sizeof(addr));
    220 	addr.enable_bit = 1;
    221 	for (bus = 0; bus < 256; ++bus) {
    222 		addr.bus_num = bus;
    223 		for (dev = 0, f = 0; dev < 32; ++dev) {
    224 			addr.dev_num = dev;
    225 			if ((r = pci_read32(&addr)) == PCI_NO_DEV)
    226 				continue;
    227 			if (!f) {
    228 				busp = create_bus();
    229 				if (IS_ERR(busp))
    230 					return PTR_ERR(busp);
    231 				list_add(&busp->pci_bus_list, &rootbus);
    232 				f = 1;
    233 			}
    234 			ret = attach_dev(busp, addr, bus, dev);
    235 			if (ret < 0) {
    236 				return ret;
    237 			}
    238 		}
    239 	}
    240 	return 0;
    241 }
    242 
    243 struct pci_dev_t *
    244 get_pci_dev(uint16_t vendor_id, uint16_t device_id) {
    245 	struct list_head *iter_bus, *iter_dev;
    246 	struct pci_dev_t *dev;
    247 	struct pci_bus_t *bus;
    248 
    249 	if (!has_pci_init) {
    250 		info("there is no PCI bus!\n");
    251 		return NULL;
    252 	}
    253 
    254 	list_for_each(iter_bus, &rootbus) {
    255 		bus = list_entry(iter_bus, struct pci_bus_t, pci_bus_list);
    256 		list_for_each(iter_dev, &bus->pci_dev_list) {
    257 			dev = list_entry(iter_dev, struct pci_dev_t, list);
    258 			if (!dev->cspace)
    259 				continue;
    260 			if (dev->cspace->type0.vendor_id == vendor_id
    261 					&& dev->cspace->type0.device_id == device_id)
    262 				return dev;
    263 		}
    264 	}
    265 	return NULL;
    266 }
    267