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 static 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 static 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