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_get_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 void pci_bar_set_addr(pcidevaddr_t dev, int bar_num, phys_addr_t addr) 48 { 49 int off = PCI_BASE_ADDRESS_0 + bar_num * 4; 50 51 pci_config_writel(dev, off, (uint32_t)addr); 52 53 if (pci_bar_is64(dev, bar_num)) 54 pci_config_writel(dev, off + 4, (uint32_t)(addr >> 32)); 55 } 56 57 /* 58 * To determine the amount of address space needed by a PCI device, 59 * one must save the original value of the BAR, write a value of 60 * all 1's to the register, and then read it back. The amount of 61 * memory can be then determined by masking the information bits, 62 * performing a bitwise NOT, and incrementing the value by 1. 63 * 64 * The following pci_bar_size_helper() and pci_bar_size() functions 65 * implement the algorithm. 66 */ 67 static uint32_t pci_bar_size_helper(pcidevaddr_t dev, int bar_num) 68 { 69 int off = PCI_BASE_ADDRESS_0 + bar_num * 4; 70 uint32_t bar, val; 71 72 bar = pci_config_readl(dev, off); 73 pci_config_writel(dev, off, ~0u); 74 val = pci_config_readl(dev, off); 75 pci_config_writel(dev, off, bar); 76 77 return val; 78 } 79 80 phys_addr_t pci_bar_size(pcidevaddr_t dev, int bar_num) 81 { 82 uint32_t bar, size; 83 84 size = pci_bar_size_helper(dev, bar_num); 85 if (!size) 86 return 0; 87 88 bar = pci_bar_get(dev, bar_num); 89 size &= pci_bar_mask(bar); 90 91 if (pci_bar_is64(dev, bar_num)) { 92 phys_addr_t size64 = pci_bar_size_helper(dev, bar_num + 1); 93 size64 = (size64 << 32) | size; 94 95 return ~size64 + 1; 96 } else { 97 return ~size + 1; 98 } 99 } 100 101 bool pci_bar_is_memory(pcidevaddr_t dev, int bar_num) 102 { 103 uint32_t bar = pci_bar_get(dev, bar_num); 104 105 return !(bar & PCI_BASE_ADDRESS_SPACE_IO); 106 } 107 108 bool pci_bar_is_valid(pcidevaddr_t dev, int bar_num) 109 { 110 return pci_bar_get(dev, bar_num); 111 } 112 113 bool pci_bar_is64(pcidevaddr_t dev, int bar_num) 114 { 115 uint32_t bar = pci_bar_get(dev, bar_num); 116 117 if (bar & PCI_BASE_ADDRESS_SPACE_IO) 118 return false; 119 120 return (bar & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == 121 PCI_BASE_ADDRESS_MEM_TYPE_64; 122 } 123