1 /* 2 * Copyright (C) 2013, Red Hat Inc, Michael S. Tsirkin <mst@redhat.com> 3 * 4 * This work is licensed under the terms of the GNU LGPL, version 2. 5 */ 6 #include <linux/pci_regs.h> 7 #include "pci.h" 8 #include "asm/pci.h" 9 10 bool pci_dev_exists(pcidevaddr_t dev) 11 { 12 return (pci_config_readw(dev, PCI_VENDOR_ID) != 0xffff && 13 pci_config_readw(dev, PCI_DEVICE_ID) != 0xffff); 14 } 15 16 /* Scan bus look for a specific device. Only bus 0 scanned for now. */ 17 pcidevaddr_t pci_find_dev(uint16_t vendor_id, uint16_t device_id) 18 { 19 pcidevaddr_t dev; 20 21 for (dev = 0; dev < 256; ++dev) { 22 if (pci_config_readw(dev, PCI_VENDOR_ID) == vendor_id && 23 pci_config_readw(dev, PCI_DEVICE_ID) == device_id) 24 return dev; 25 } 26 27 return PCIDEVADDR_INVALID; 28 } 29 30 uint32_t pci_bar_mask(uint32_t bar) 31 { 32 return (bar & PCI_BASE_ADDRESS_SPACE_IO) ? 33 PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK; 34 } 35 36 uint32_t pci_bar_get(pcidevaddr_t dev, int bar_num) 37 { 38 return pci_config_readl(dev, PCI_BASE_ADDRESS_0 + bar_num * 4); 39 } 40 41 phys_addr_t pci_bar_get_addr(pcidevaddr_t dev, int bar_num) 42 { 43 uint32_t bar = pci_bar_get(dev, bar_num); 44 uint32_t mask = pci_bar_mask(bar); 45 uint64_t addr = bar & mask; 46 phys_addr_t phys_addr; 47 48 if (pci_bar_is64(dev, bar_num)) 49 addr |= (uint64_t)pci_bar_get(dev, bar_num + 1) << 32; 50 51 phys_addr = pci_translate_addr(dev, addr); 52 assert(phys_addr != INVALID_PHYS_ADDR); 53 54 return phys_addr; 55 } 56 57 void pci_bar_set_addr(pcidevaddr_t dev, int bar_num, phys_addr_t addr) 58 { 59 int off = PCI_BASE_ADDRESS_0 + bar_num * 4; 60 61 pci_config_writel(dev, off, (uint32_t)addr); 62 63 if (pci_bar_is64(dev, bar_num)) 64 pci_config_writel(dev, off + 4, (uint32_t)(addr >> 32)); 65 } 66 67 /* 68 * To determine the amount of address space needed by a PCI device, 69 * one must save the original value of the BAR, write a value of 70 * all 1's to the register, and then read it back. The amount of 71 * memory can be then determined by masking the information bits, 72 * performing a bitwise NOT, and incrementing the value by 1. 73 * 74 * The following pci_bar_size_helper() and pci_bar_size() functions 75 * implement the algorithm. 76 */ 77 static uint32_t pci_bar_size_helper(pcidevaddr_t dev, int bar_num) 78 { 79 int off = PCI_BASE_ADDRESS_0 + bar_num * 4; 80 uint32_t bar, val; 81 82 bar = pci_config_readl(dev, off); 83 pci_config_writel(dev, off, ~0u); 84 val = pci_config_readl(dev, off); 85 pci_config_writel(dev, off, bar); 86 87 return val; 88 } 89 90 phys_addr_t pci_bar_size(pcidevaddr_t dev, int bar_num) 91 { 92 uint32_t bar, size; 93 94 size = pci_bar_size_helper(dev, bar_num); 95 if (!size) 96 return 0; 97 98 bar = pci_bar_get(dev, bar_num); 99 size &= pci_bar_mask(bar); 100 101 if (pci_bar_is64(dev, bar_num)) { 102 phys_addr_t size64 = pci_bar_size_helper(dev, bar_num + 1); 103 size64 = (size64 << 32) | size; 104 105 return ~size64 + 1; 106 } else { 107 return ~size + 1; 108 } 109 } 110 111 bool pci_bar_is_memory(pcidevaddr_t dev, int bar_num) 112 { 113 uint32_t bar = pci_bar_get(dev, bar_num); 114 115 return !(bar & PCI_BASE_ADDRESS_SPACE_IO); 116 } 117 118 bool pci_bar_is_valid(pcidevaddr_t dev, int bar_num) 119 { 120 return pci_bar_get(dev, bar_num); 121 } 122 123 bool pci_bar_is64(pcidevaddr_t dev, int bar_num) 124 { 125 uint32_t bar = pci_bar_get(dev, bar_num); 126 127 if (bar & PCI_BASE_ADDRESS_SPACE_IO) 128 return false; 129 130 return (bar & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == 131 PCI_BASE_ADDRESS_MEM_TYPE_64; 132 } 133 134 void pci_bar_print(pcidevaddr_t dev, int bar_num) 135 { 136 phys_addr_t size, start, end; 137 uint32_t bar; 138 139 size = pci_bar_size(dev, bar_num); 140 if (!size) 141 return; 142 143 bar = pci_bar_get(dev, bar_num); 144 start = pci_bar_get_addr(dev, bar_num); 145 end = start + size - 1; 146 147 if (pci_bar_is64(dev, bar_num)) { 148 printf("BAR#%d,%d [%" PRIx64 "-%" PRIx64 " ", 149 bar_num, bar_num + 1, start, end); 150 } else { 151 printf("BAR#%d [%02x-%02x ", 152 bar_num, (uint32_t)start, (uint32_t)end); 153 } 154 155 if (bar & PCI_BASE_ADDRESS_SPACE_IO) { 156 printf("PIO"); 157 } else { 158 printf("MEM"); 159 switch (bar & PCI_BASE_ADDRESS_MEM_TYPE_MASK) { 160 case PCI_BASE_ADDRESS_MEM_TYPE_32: 161 printf("32"); 162 break; 163 case PCI_BASE_ADDRESS_MEM_TYPE_1M: 164 printf("1M"); 165 break; 166 case PCI_BASE_ADDRESS_MEM_TYPE_64: 167 printf("64"); 168 break; 169 default: 170 assert(0); 171 } 172 } 173 174 if (bar & PCI_BASE_ADDRESS_MEM_PREFETCH) 175 printf("/p"); 176 177 printf("]"); 178 } 179 180 void pci_dev_print_id(pcidevaddr_t dev) 181 { 182 printf("00.%02x.%1x %04x:%04x", dev / 8, dev % 8, 183 pci_config_readw(dev, PCI_VENDOR_ID), 184 pci_config_readw(dev, PCI_DEVICE_ID)); 185 } 186 187 static void pci_dev_print(pcidevaddr_t dev) 188 { 189 uint8_t header = pci_config_readb(dev, PCI_HEADER_TYPE); 190 uint8_t progif = pci_config_readb(dev, PCI_CLASS_PROG); 191 uint8_t subclass = pci_config_readb(dev, PCI_CLASS_DEVICE); 192 uint8_t class = pci_config_readb(dev, PCI_CLASS_DEVICE + 1); 193 int i; 194 195 pci_dev_print_id(dev); 196 printf(" type %02x progif %02x class %02x subclass %02x\n", 197 header, progif, class, subclass); 198 199 if ((header & PCI_HEADER_TYPE_MASK) != PCI_HEADER_TYPE_NORMAL) 200 return; 201 202 for (i = 0; i < 6; i++) { 203 if (pci_bar_size(dev, i)) { 204 printf("\t"); 205 pci_bar_print(dev, i); 206 printf("\n"); 207 } 208 if (pci_bar_is64(dev, i)) 209 i++; 210 } 211 } 212 213 void pci_print(void) 214 { 215 pcidevaddr_t dev; 216 217 for (dev = 0; dev < 256; ++dev) { 218 if (pci_dev_exists(dev)) 219 pci_dev_print(dev); 220 } 221 } 222