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