1456c55bcSAndrew Jones /* 2456c55bcSAndrew Jones * Copyright (C) 2013, Red Hat Inc, Michael S. Tsirkin <mst@redhat.com> 3456c55bcSAndrew Jones * 4456c55bcSAndrew Jones * This work is licensed under the terms of the GNU LGPL, version 2. 5456c55bcSAndrew Jones */ 64932b58aSMichael S. Tsirkin #include <linux/pci_regs.h> 74932b58aSMichael S. Tsirkin #include "pci.h" 8456c55bcSAndrew Jones #include "asm/pci.h" 94932b58aSMichael S. Tsirkin 10*66082ed6SPeter Xu void pci_cmd_set_clr(struct pci_dev *dev, uint16_t set, uint16_t clr) 11*66082ed6SPeter Xu { 12*66082ed6SPeter Xu uint16_t val = pci_config_readw(dev->bdf, PCI_COMMAND); 13*66082ed6SPeter Xu 14*66082ed6SPeter Xu /* No overlap is allowed */ 15*66082ed6SPeter Xu assert((set & clr) == 0); 16*66082ed6SPeter Xu val |= set; 17*66082ed6SPeter Xu val &= ~clr; 18*66082ed6SPeter Xu 19*66082ed6SPeter Xu pci_config_writew(dev->bdf, PCI_COMMAND, val); 20*66082ed6SPeter Xu } 21*66082ed6SPeter Xu 22e1cad5c8SAlexander Gordeev bool pci_dev_exists(pcidevaddr_t dev) 23e1cad5c8SAlexander Gordeev { 24e1cad5c8SAlexander Gordeev return (pci_config_readw(dev, PCI_VENDOR_ID) != 0xffff && 25e1cad5c8SAlexander Gordeev pci_config_readw(dev, PCI_DEVICE_ID) != 0xffff); 26e1cad5c8SAlexander Gordeev } 27e1cad5c8SAlexander Gordeev 284d6cefa9SPeter Xu void pci_dev_init(struct pci_dev *dev, pcidevaddr_t bdf) 294d6cefa9SPeter Xu { 304d6cefa9SPeter Xu memset(dev, 0, sizeof(*dev)); 314d6cefa9SPeter Xu dev->bdf = bdf; 324d6cefa9SPeter Xu } 334d6cefa9SPeter Xu 344932b58aSMichael S. Tsirkin /* Scan bus look for a specific device. Only bus 0 scanned for now. */ 354932b58aSMichael S. Tsirkin pcidevaddr_t pci_find_dev(uint16_t vendor_id, uint16_t device_id) 364932b58aSMichael S. Tsirkin { 37ebb58e7eSAlexander Gordeev pcidevaddr_t dev; 38ebb58e7eSAlexander Gordeev 394d6cefa9SPeter Xu for (dev = 0; dev < PCI_DEVFN_MAX; ++dev) { 40d8369c77SAlexander Gordeev if (pci_config_readw(dev, PCI_VENDOR_ID) == vendor_id && 41d8369c77SAlexander Gordeev pci_config_readw(dev, PCI_DEVICE_ID) == device_id) 424932b58aSMichael S. Tsirkin return dev; 434932b58aSMichael S. Tsirkin } 44ebb58e7eSAlexander Gordeev 454932b58aSMichael S. Tsirkin return PCIDEVADDR_INVALID; 464932b58aSMichael S. Tsirkin } 474932b58aSMichael S. Tsirkin 4833d78b07SAlexander Gordeev uint32_t pci_bar_mask(uint32_t bar) 492455ef20SAlexander Gordeev { 502455ef20SAlexander Gordeev return (bar & PCI_BASE_ADDRESS_SPACE_IO) ? 512455ef20SAlexander Gordeev PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK; 522455ef20SAlexander Gordeev } 532455ef20SAlexander Gordeev 544d6cefa9SPeter Xu uint32_t pci_bar_get(struct pci_dev *dev, int bar_num) 557aa83307SAlexander Gordeev { 564d6cefa9SPeter Xu return pci_config_readl(dev->bdf, PCI_BASE_ADDRESS_0 + 574d6cefa9SPeter Xu bar_num * 4); 587aa83307SAlexander Gordeev } 597aa83307SAlexander Gordeev 604d6cefa9SPeter Xu phys_addr_t pci_bar_get_addr(struct pci_dev *dev, int bar_num) 614932b58aSMichael S. Tsirkin { 627aa83307SAlexander Gordeev uint32_t bar = pci_bar_get(dev, bar_num); 632455ef20SAlexander Gordeev uint32_t mask = pci_bar_mask(bar); 642455ef20SAlexander Gordeev uint64_t addr = bar & mask; 65647d2ab7SAlexander Gordeev phys_addr_t phys_addr; 66ebb58e7eSAlexander Gordeev 672455ef20SAlexander Gordeev if (pci_bar_is64(dev, bar_num)) 682455ef20SAlexander Gordeev addr |= (uint64_t)pci_bar_get(dev, bar_num + 1) << 32; 692455ef20SAlexander Gordeev 704d6cefa9SPeter Xu phys_addr = pci_translate_addr(dev->bdf, addr); 71647d2ab7SAlexander Gordeev assert(phys_addr != INVALID_PHYS_ADDR); 72647d2ab7SAlexander Gordeev 73647d2ab7SAlexander Gordeev return phys_addr; 742455ef20SAlexander Gordeev } 752455ef20SAlexander Gordeev 764d6cefa9SPeter Xu void pci_bar_set_addr(struct pci_dev *dev, int bar_num, phys_addr_t addr) 77647f92c7SAlexander Gordeev { 78647f92c7SAlexander Gordeev int off = PCI_BASE_ADDRESS_0 + bar_num * 4; 79647f92c7SAlexander Gordeev 804d6cefa9SPeter Xu pci_config_writel(dev->bdf, off, (uint32_t)addr); 81647f92c7SAlexander Gordeev 82647f92c7SAlexander Gordeev if (pci_bar_is64(dev, bar_num)) 834d6cefa9SPeter Xu pci_config_writel(dev->bdf, off + 4, 844d6cefa9SPeter Xu (uint32_t)(addr >> 32)); 85647f92c7SAlexander Gordeev } 86647f92c7SAlexander Gordeev 872455ef20SAlexander Gordeev /* 882455ef20SAlexander Gordeev * To determine the amount of address space needed by a PCI device, 892455ef20SAlexander Gordeev * one must save the original value of the BAR, write a value of 902455ef20SAlexander Gordeev * all 1's to the register, and then read it back. The amount of 912455ef20SAlexander Gordeev * memory can be then determined by masking the information bits, 922455ef20SAlexander Gordeev * performing a bitwise NOT, and incrementing the value by 1. 932455ef20SAlexander Gordeev * 942455ef20SAlexander Gordeev * The following pci_bar_size_helper() and pci_bar_size() functions 952455ef20SAlexander Gordeev * implement the algorithm. 962455ef20SAlexander Gordeev */ 974d6cefa9SPeter Xu static uint32_t pci_bar_size_helper(struct pci_dev *dev, int bar_num) 982455ef20SAlexander Gordeev { 992455ef20SAlexander Gordeev int off = PCI_BASE_ADDRESS_0 + bar_num * 4; 1004d6cefa9SPeter Xu uint16_t bdf = dev->bdf; 1012455ef20SAlexander Gordeev uint32_t bar, val; 1022455ef20SAlexander Gordeev 1034d6cefa9SPeter Xu bar = pci_config_readl(bdf, off); 1044d6cefa9SPeter Xu pci_config_writel(bdf, off, ~0u); 1054d6cefa9SPeter Xu val = pci_config_readl(bdf, off); 1064d6cefa9SPeter Xu pci_config_writel(bdf, off, bar); 1072455ef20SAlexander Gordeev 1082455ef20SAlexander Gordeev return val; 1092455ef20SAlexander Gordeev } 1102455ef20SAlexander Gordeev 1114d6cefa9SPeter Xu phys_addr_t pci_bar_size(struct pci_dev *dev, int bar_num) 1122455ef20SAlexander Gordeev { 1132455ef20SAlexander Gordeev uint32_t bar, size; 1142455ef20SAlexander Gordeev 1152455ef20SAlexander Gordeev size = pci_bar_size_helper(dev, bar_num); 1162455ef20SAlexander Gordeev if (!size) 1172455ef20SAlexander Gordeev return 0; 1182455ef20SAlexander Gordeev 1192455ef20SAlexander Gordeev bar = pci_bar_get(dev, bar_num); 1202455ef20SAlexander Gordeev size &= pci_bar_mask(bar); 1212455ef20SAlexander Gordeev 1222455ef20SAlexander Gordeev if (pci_bar_is64(dev, bar_num)) { 1232455ef20SAlexander Gordeev phys_addr_t size64 = pci_bar_size_helper(dev, bar_num + 1); 1242455ef20SAlexander Gordeev size64 = (size64 << 32) | size; 1252455ef20SAlexander Gordeev 1262455ef20SAlexander Gordeev return ~size64 + 1; 1272455ef20SAlexander Gordeev } else { 1282455ef20SAlexander Gordeev return ~size + 1; 1292455ef20SAlexander Gordeev } 1304932b58aSMichael S. Tsirkin } 1314932b58aSMichael S. Tsirkin 1324d6cefa9SPeter Xu bool pci_bar_is_memory(struct pci_dev *dev, int bar_num) 1334932b58aSMichael S. Tsirkin { 1347aa83307SAlexander Gordeev uint32_t bar = pci_bar_get(dev, bar_num); 135ebb58e7eSAlexander Gordeev 1364932b58aSMichael S. Tsirkin return !(bar & PCI_BASE_ADDRESS_SPACE_IO); 1374932b58aSMichael S. Tsirkin } 1384932b58aSMichael S. Tsirkin 1394d6cefa9SPeter Xu bool pci_bar_is_valid(struct pci_dev *dev, int bar_num) 1404932b58aSMichael S. Tsirkin { 1417aa83307SAlexander Gordeev return pci_bar_get(dev, bar_num); 1424932b58aSMichael S. Tsirkin } 1432455ef20SAlexander Gordeev 1444d6cefa9SPeter Xu bool pci_bar_is64(struct pci_dev *dev, int bar_num) 1452455ef20SAlexander Gordeev { 1462455ef20SAlexander Gordeev uint32_t bar = pci_bar_get(dev, bar_num); 1472455ef20SAlexander Gordeev 1482455ef20SAlexander Gordeev if (bar & PCI_BASE_ADDRESS_SPACE_IO) 1492455ef20SAlexander Gordeev return false; 1502455ef20SAlexander Gordeev 1512455ef20SAlexander Gordeev return (bar & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == 1522455ef20SAlexander Gordeev PCI_BASE_ADDRESS_MEM_TYPE_64; 1532455ef20SAlexander Gordeev } 154e4611520SAlexander Gordeev 1554d6cefa9SPeter Xu void pci_bar_print(struct pci_dev *dev, int bar_num) 156e4611520SAlexander Gordeev { 157e4611520SAlexander Gordeev phys_addr_t size, start, end; 158e4611520SAlexander Gordeev uint32_t bar; 159e4611520SAlexander Gordeev 160e4611520SAlexander Gordeev size = pci_bar_size(dev, bar_num); 161e4611520SAlexander Gordeev if (!size) 162e4611520SAlexander Gordeev return; 163e4611520SAlexander Gordeev 164e4611520SAlexander Gordeev bar = pci_bar_get(dev, bar_num); 165e4611520SAlexander Gordeev start = pci_bar_get_addr(dev, bar_num); 166e4611520SAlexander Gordeev end = start + size - 1; 167e4611520SAlexander Gordeev 168e4611520SAlexander Gordeev if (pci_bar_is64(dev, bar_num)) { 169e4611520SAlexander Gordeev printf("BAR#%d,%d [%" PRIx64 "-%" PRIx64 " ", 170e4611520SAlexander Gordeev bar_num, bar_num + 1, start, end); 171e4611520SAlexander Gordeev } else { 172e4611520SAlexander Gordeev printf("BAR#%d [%02x-%02x ", 173e4611520SAlexander Gordeev bar_num, (uint32_t)start, (uint32_t)end); 174e4611520SAlexander Gordeev } 175e4611520SAlexander Gordeev 176e4611520SAlexander Gordeev if (bar & PCI_BASE_ADDRESS_SPACE_IO) { 177e4611520SAlexander Gordeev printf("PIO"); 178e4611520SAlexander Gordeev } else { 179e4611520SAlexander Gordeev printf("MEM"); 180e4611520SAlexander Gordeev switch (bar & PCI_BASE_ADDRESS_MEM_TYPE_MASK) { 181e4611520SAlexander Gordeev case PCI_BASE_ADDRESS_MEM_TYPE_32: 182e4611520SAlexander Gordeev printf("32"); 183e4611520SAlexander Gordeev break; 184e4611520SAlexander Gordeev case PCI_BASE_ADDRESS_MEM_TYPE_1M: 185e4611520SAlexander Gordeev printf("1M"); 186e4611520SAlexander Gordeev break; 187e4611520SAlexander Gordeev case PCI_BASE_ADDRESS_MEM_TYPE_64: 188e4611520SAlexander Gordeev printf("64"); 189e4611520SAlexander Gordeev break; 190e4611520SAlexander Gordeev default: 191e4611520SAlexander Gordeev assert(0); 192e4611520SAlexander Gordeev } 193e4611520SAlexander Gordeev } 194e4611520SAlexander Gordeev 195e4611520SAlexander Gordeev if (bar & PCI_BASE_ADDRESS_MEM_PREFETCH) 196e4611520SAlexander Gordeev printf("/p"); 197e4611520SAlexander Gordeev 198e4611520SAlexander Gordeev printf("]"); 199e4611520SAlexander Gordeev } 200e4611520SAlexander Gordeev 20133d78b07SAlexander Gordeev void pci_dev_print_id(pcidevaddr_t dev) 202e4611520SAlexander Gordeev { 203e4611520SAlexander Gordeev printf("00.%02x.%1x %04x:%04x", dev / 8, dev % 8, 204e4611520SAlexander Gordeev pci_config_readw(dev, PCI_VENDOR_ID), 205e4611520SAlexander Gordeev pci_config_readw(dev, PCI_DEVICE_ID)); 206e4611520SAlexander Gordeev } 207e4611520SAlexander Gordeev 208e4611520SAlexander Gordeev static void pci_dev_print(pcidevaddr_t dev) 209e4611520SAlexander Gordeev { 210e4611520SAlexander Gordeev uint8_t header = pci_config_readb(dev, PCI_HEADER_TYPE); 211e4611520SAlexander Gordeev uint8_t progif = pci_config_readb(dev, PCI_CLASS_PROG); 212e4611520SAlexander Gordeev uint8_t subclass = pci_config_readb(dev, PCI_CLASS_DEVICE); 213e4611520SAlexander Gordeev uint8_t class = pci_config_readb(dev, PCI_CLASS_DEVICE + 1); 2144d6cefa9SPeter Xu struct pci_dev pci_dev; 215e4611520SAlexander Gordeev int i; 216e4611520SAlexander Gordeev 2174d6cefa9SPeter Xu pci_dev_init(&pci_dev, dev); 2184d6cefa9SPeter Xu 219e4611520SAlexander Gordeev pci_dev_print_id(dev); 220e4611520SAlexander Gordeev printf(" type %02x progif %02x class %02x subclass %02x\n", 221e4611520SAlexander Gordeev header, progif, class, subclass); 222e4611520SAlexander Gordeev 223e4611520SAlexander Gordeev if ((header & PCI_HEADER_TYPE_MASK) != PCI_HEADER_TYPE_NORMAL) 224e4611520SAlexander Gordeev return; 225e4611520SAlexander Gordeev 226e954ce23SPeter Xu for (i = 0; i < PCI_BAR_NUM; i++) { 2274d6cefa9SPeter Xu if (pci_bar_size(&pci_dev, i)) { 228e4611520SAlexander Gordeev printf("\t"); 2294d6cefa9SPeter Xu pci_bar_print(&pci_dev, i); 230e4611520SAlexander Gordeev printf("\n"); 231e4611520SAlexander Gordeev } 2324d6cefa9SPeter Xu if (pci_bar_is64(&pci_dev, i)) 233e4611520SAlexander Gordeev i++; 234e4611520SAlexander Gordeev } 235e4611520SAlexander Gordeev } 236e4611520SAlexander Gordeev 237e4611520SAlexander Gordeev void pci_print(void) 238e4611520SAlexander Gordeev { 239e4611520SAlexander Gordeev pcidevaddr_t dev; 240e4611520SAlexander Gordeev 2414d6cefa9SPeter Xu for (dev = 0; dev < PCI_DEVFN_MAX; ++dev) { 242e4611520SAlexander Gordeev if (pci_dev_exists(dev)) 243e4611520SAlexander Gordeev pci_dev_print(dev); 244e4611520SAlexander Gordeev } 245e4611520SAlexander Gordeev } 246e954ce23SPeter Xu 247e954ce23SPeter Xu void pci_scan_bars(struct pci_dev *dev) 248e954ce23SPeter Xu { 249e954ce23SPeter Xu int i; 250e954ce23SPeter Xu 251e954ce23SPeter Xu for (i = 0; i < PCI_BAR_NUM; i++) { 252e954ce23SPeter Xu if (!pci_bar_is_valid(dev, i)) 253e954ce23SPeter Xu continue; 254e954ce23SPeter Xu dev->resource[i] = pci_bar_get_addr(dev, i); 255e954ce23SPeter Xu if (pci_bar_is64(dev, i)) { 256e954ce23SPeter Xu i++; 257e954ce23SPeter Xu dev->resource[i] = (phys_addr_t)0; 258e954ce23SPeter Xu } 259e954ce23SPeter Xu } 260e954ce23SPeter Xu } 261*66082ed6SPeter Xu 262*66082ed6SPeter Xu void pci_enable_defaults(struct pci_dev *dev) 263*66082ed6SPeter Xu { 264*66082ed6SPeter Xu pci_scan_bars(dev); 265*66082ed6SPeter Xu /* Enable device DMA operations */ 266*66082ed6SPeter Xu pci_cmd_set_clr(dev, PCI_COMMAND_MASTER, 0); 267*66082ed6SPeter Xu } 268