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 47 if (pci_bar_is64(dev, bar_num)) 48 addr |= (uint64_t)pci_bar_get(dev, bar_num + 1) << 32; 49 50 return pci_translate_addr(dev, addr); 51 } 52 53 void pci_bar_set_addr(pcidevaddr_t dev, int bar_num, phys_addr_t addr) 54 { 55 int off = PCI_BASE_ADDRESS_0 + bar_num * 4; 56 57 pci_config_writel(dev, off, (uint32_t)addr); 58 59 if (pci_bar_is64(dev, bar_num)) 60 pci_config_writel(dev, off + 4, (uint32_t)(addr >> 32)); 61 } 62 63 /* 64 * To determine the amount of address space needed by a PCI device, 65 * one must save the original value of the BAR, write a value of 66 * all 1's to the register, and then read it back. The amount of 67 * memory can be then determined by masking the information bits, 68 * performing a bitwise NOT, and incrementing the value by 1. 69 * 70 * The following pci_bar_size_helper() and pci_bar_size() functions 71 * implement the algorithm. 72 */ 73 static uint32_t pci_bar_size_helper(pcidevaddr_t dev, int bar_num) 74 { 75 int off = PCI_BASE_ADDRESS_0 + bar_num * 4; 76 uint32_t bar, val; 77 78 bar = pci_config_readl(dev, off); 79 pci_config_writel(dev, off, ~0u); 80 val = pci_config_readl(dev, off); 81 pci_config_writel(dev, off, bar); 82 83 return val; 84 } 85 86 phys_addr_t pci_bar_size(pcidevaddr_t dev, int bar_num) 87 { 88 uint32_t bar, size; 89 90 size = pci_bar_size_helper(dev, bar_num); 91 if (!size) 92 return 0; 93 94 bar = pci_bar_get(dev, bar_num); 95 size &= pci_bar_mask(bar); 96 97 if (pci_bar_is64(dev, bar_num)) { 98 phys_addr_t size64 = pci_bar_size_helper(dev, bar_num + 1); 99 size64 = (size64 << 32) | size; 100 101 return ~size64 + 1; 102 } else { 103 return ~size + 1; 104 } 105 } 106 107 bool pci_bar_is_memory(pcidevaddr_t dev, int bar_num) 108 { 109 uint32_t bar = pci_bar_get(dev, bar_num); 110 111 return !(bar & PCI_BASE_ADDRESS_SPACE_IO); 112 } 113 114 bool pci_bar_is_valid(pcidevaddr_t dev, int bar_num) 115 { 116 return pci_bar_get(dev, bar_num); 117 } 118 119 bool pci_bar_is64(pcidevaddr_t dev, int bar_num) 120 { 121 uint32_t bar = pci_bar_get(dev, bar_num); 122 123 if (bar & PCI_BASE_ADDRESS_SPACE_IO) 124 return false; 125 126 return (bar & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == 127 PCI_BASE_ADDRESS_MEM_TYPE_64; 128 } 129 130 void pci_bar_print(pcidevaddr_t dev, int bar_num) 131 { 132 phys_addr_t size, start, end; 133 uint32_t bar; 134 135 size = pci_bar_size(dev, bar_num); 136 if (!size) 137 return; 138 139 bar = pci_bar_get(dev, bar_num); 140 start = pci_bar_get_addr(dev, bar_num); 141 end = start + size - 1; 142 143 if (pci_bar_is64(dev, bar_num)) { 144 printf("BAR#%d,%d [%" PRIx64 "-%" PRIx64 " ", 145 bar_num, bar_num + 1, start, end); 146 } else { 147 printf("BAR#%d [%02x-%02x ", 148 bar_num, (uint32_t)start, (uint32_t)end); 149 } 150 151 if (bar & PCI_BASE_ADDRESS_SPACE_IO) { 152 printf("PIO"); 153 } else { 154 printf("MEM"); 155 switch (bar & PCI_BASE_ADDRESS_MEM_TYPE_MASK) { 156 case PCI_BASE_ADDRESS_MEM_TYPE_32: 157 printf("32"); 158 break; 159 case PCI_BASE_ADDRESS_MEM_TYPE_1M: 160 printf("1M"); 161 break; 162 case PCI_BASE_ADDRESS_MEM_TYPE_64: 163 printf("64"); 164 break; 165 default: 166 assert(0); 167 } 168 } 169 170 if (bar & PCI_BASE_ADDRESS_MEM_PREFETCH) 171 printf("/p"); 172 173 printf("]"); 174 } 175 176 void pci_dev_print_id(pcidevaddr_t dev) 177 { 178 printf("00.%02x.%1x %04x:%04x", dev / 8, dev % 8, 179 pci_config_readw(dev, PCI_VENDOR_ID), 180 pci_config_readw(dev, PCI_DEVICE_ID)); 181 } 182 183 static void pci_dev_print(pcidevaddr_t dev) 184 { 185 uint8_t header = pci_config_readb(dev, PCI_HEADER_TYPE); 186 uint8_t progif = pci_config_readb(dev, PCI_CLASS_PROG); 187 uint8_t subclass = pci_config_readb(dev, PCI_CLASS_DEVICE); 188 uint8_t class = pci_config_readb(dev, PCI_CLASS_DEVICE + 1); 189 int i; 190 191 pci_dev_print_id(dev); 192 printf(" type %02x progif %02x class %02x subclass %02x\n", 193 header, progif, class, subclass); 194 195 if ((header & PCI_HEADER_TYPE_MASK) != PCI_HEADER_TYPE_NORMAL) 196 return; 197 198 for (i = 0; i < 6; i++) { 199 if (pci_bar_size(dev, i)) { 200 printf("\t"); 201 pci_bar_print(dev, i); 202 printf("\n"); 203 } 204 if (pci_bar_is64(dev, i)) 205 i++; 206 } 207 } 208 209 void pci_print(void) 210 { 211 pcidevaddr_t dev; 212 213 for (dev = 0; dev < 256; ++dev) { 214 if (pci_dev_exists(dev)) 215 pci_dev_print(dev); 216 } 217 } 218