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 /* Scan bus look for a specific device. Only bus 0 scanned for now. */ 11 pcidevaddr_t pci_find_dev(uint16_t vendor_id, uint16_t device_id) 12 { 13 pcidevaddr_t dev; 14 15 for (dev = 0; dev < 256; ++dev) { 16 if (pci_config_readw(dev, PCI_VENDOR_ID) == vendor_id && 17 pci_config_readw(dev, PCI_DEVICE_ID) == device_id) 18 return dev; 19 } 20 21 return PCIDEVADDR_INVALID; 22 } 23 24 static uint32_t pci_bar_mask(uint32_t bar) 25 { 26 return (bar & PCI_BASE_ADDRESS_SPACE_IO) ? 27 PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK; 28 } 29 30 static uint32_t pci_bar_get(pcidevaddr_t dev, int bar_num) 31 { 32 return pci_config_readl(dev, PCI_BASE_ADDRESS_0 + bar_num * 4); 33 } 34 35 phys_addr_t pci_bar_addr(pcidevaddr_t dev, int bar_num) 36 { 37 uint32_t bar = pci_bar_get(dev, bar_num); 38 uint32_t mask = pci_bar_mask(bar); 39 uint64_t addr = bar & mask; 40 41 if (pci_bar_is64(dev, bar_num)) 42 addr |= (uint64_t)pci_bar_get(dev, bar_num + 1) << 32; 43 44 return pci_translate_addr(dev, addr); 45 } 46 47 /* 48 * To determine the amount of address space needed by a PCI device, 49 * one must save the original value of the BAR, write a value of 50 * all 1's to the register, and then read it back. The amount of 51 * memory can be then determined by masking the information bits, 52 * performing a bitwise NOT, and incrementing the value by 1. 53 * 54 * The following pci_bar_size_helper() and pci_bar_size() functions 55 * implement the algorithm. 56 */ 57 static uint32_t pci_bar_size_helper(pcidevaddr_t dev, int bar_num) 58 { 59 int off = PCI_BASE_ADDRESS_0 + bar_num * 4; 60 uint32_t bar, val; 61 62 bar = pci_config_readl(dev, off); 63 pci_config_writel(dev, off, ~0u); 64 val = pci_config_readl(dev, off); 65 pci_config_writel(dev, off, bar); 66 67 return val; 68 } 69 70 phys_addr_t pci_bar_size(pcidevaddr_t dev, int bar_num) 71 { 72 uint32_t bar, size; 73 74 size = pci_bar_size_helper(dev, bar_num); 75 if (!size) 76 return 0; 77 78 bar = pci_bar_get(dev, bar_num); 79 size &= pci_bar_mask(bar); 80 81 if (pci_bar_is64(dev, bar_num)) { 82 phys_addr_t size64 = pci_bar_size_helper(dev, bar_num + 1); 83 size64 = (size64 << 32) | size; 84 85 return ~size64 + 1; 86 } else { 87 return ~size + 1; 88 } 89 } 90 91 bool pci_bar_is_memory(pcidevaddr_t dev, int bar_num) 92 { 93 uint32_t bar = pci_bar_get(dev, bar_num); 94 95 return !(bar & PCI_BASE_ADDRESS_SPACE_IO); 96 } 97 98 bool pci_bar_is_valid(pcidevaddr_t dev, int bar_num) 99 { 100 return pci_bar_get(dev, bar_num); 101 } 102 103 bool pci_bar_is64(pcidevaddr_t dev, int bar_num) 104 { 105 uint32_t bar = pci_bar_get(dev, bar_num); 106 107 if (bar & PCI_BASE_ADDRESS_SPACE_IO) 108 return false; 109 110 return (bar & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == 111 PCI_BASE_ADDRESS_MEM_TYPE_64; 112 } 113