1 /* 2 * pcie_host.c 3 * utility functions for pci express host bridge. 4 * 5 * Copyright (c) 2009 Isaku Yamahata <yamahata at valinux co jp> 6 * VA Linux Systems Japan K.K. 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 18 * You should have received a copy of the GNU General Public License along 19 * with this program; if not, see <http://www.gnu.org/licenses/>. 20 */ 21 22 #include "hw/hw.h" 23 #include "hw/pci/pci.h" 24 #include "hw/pci/pcie_host.h" 25 #include "exec/address-spaces.h" 26 27 /* 28 * PCI express mmcfig address 29 * bit 20 - 28: bus number 30 * bit 15 - 19: device number 31 * bit 12 - 14: function number 32 * bit 0 - 11: offset in configuration space of a given device 33 */ 34 #define PCIE_MMCFG_SIZE_MAX (1ULL << 28) 35 #define PCIE_MMCFG_SIZE_MIN (1ULL << 20) 36 #define PCIE_MMCFG_BUS_BIT 20 37 #define PCIE_MMCFG_BUS_MASK 0x1ff 38 #define PCIE_MMCFG_DEVFN_BIT 12 39 #define PCIE_MMCFG_DEVFN_MASK 0xff 40 #define PCIE_MMCFG_CONFOFFSET_MASK 0xfff 41 #define PCIE_MMCFG_BUS(addr) (((addr) >> PCIE_MMCFG_BUS_BIT) & \ 42 PCIE_MMCFG_BUS_MASK) 43 #define PCIE_MMCFG_DEVFN(addr) (((addr) >> PCIE_MMCFG_DEVFN_BIT) & \ 44 PCIE_MMCFG_DEVFN_MASK) 45 #define PCIE_MMCFG_CONFOFFSET(addr) ((addr) & PCIE_MMCFG_CONFOFFSET_MASK) 46 47 48 /* a helper function to get a PCIDevice for a given mmconfig address */ 49 static inline PCIDevice *pcie_dev_find_by_mmcfg_addr(PCIBus *s, 50 uint32_t mmcfg_addr) 51 { 52 return pci_find_device(s, PCIE_MMCFG_BUS(mmcfg_addr), 53 PCIE_MMCFG_DEVFN(mmcfg_addr)); 54 } 55 56 static void pcie_mmcfg_data_write(void *opaque, hwaddr mmcfg_addr, 57 uint64_t val, unsigned len) 58 { 59 PCIExpressHost *e = opaque; 60 PCIBus *s = e->pci.bus; 61 PCIDevice *pci_dev = pcie_dev_find_by_mmcfg_addr(s, mmcfg_addr); 62 uint32_t addr; 63 uint32_t limit; 64 65 if (!pci_dev) { 66 return; 67 } 68 addr = PCIE_MMCFG_CONFOFFSET(mmcfg_addr); 69 limit = pci_config_size(pci_dev); 70 if (limit <= addr) { 71 /* conventional pci device can be behind pcie-to-pci bridge. 72 256 <= addr < 4K has no effects. */ 73 return; 74 } 75 pci_host_config_write_common(pci_dev, addr, limit, val, len); 76 } 77 78 static uint64_t pcie_mmcfg_data_read(void *opaque, 79 hwaddr mmcfg_addr, 80 unsigned len) 81 { 82 PCIExpressHost *e = opaque; 83 PCIBus *s = e->pci.bus; 84 PCIDevice *pci_dev = pcie_dev_find_by_mmcfg_addr(s, mmcfg_addr); 85 uint32_t addr; 86 uint32_t limit; 87 88 if (!pci_dev) { 89 return ~0x0; 90 } 91 addr = PCIE_MMCFG_CONFOFFSET(mmcfg_addr); 92 limit = pci_config_size(pci_dev); 93 if (limit <= addr) { 94 /* conventional pci device can be behind pcie-to-pci bridge. 95 256 <= addr < 4K has no effects. */ 96 return ~0x0; 97 } 98 return pci_host_config_read_common(pci_dev, addr, limit, len); 99 } 100 101 static const MemoryRegionOps pcie_mmcfg_ops = { 102 .read = pcie_mmcfg_data_read, 103 .write = pcie_mmcfg_data_write, 104 .endianness = DEVICE_NATIVE_ENDIAN, 105 }; 106 107 /* pcie_host::base_addr == PCIE_BASE_ADDR_UNMAPPED when it isn't mapped. */ 108 #define PCIE_BASE_ADDR_UNMAPPED ((hwaddr)-1ULL) 109 110 int pcie_host_init(PCIExpressHost *e) 111 { 112 e->base_addr = PCIE_BASE_ADDR_UNMAPPED; 113 114 return 0; 115 } 116 117 void pcie_host_mmcfg_unmap(PCIExpressHost *e) 118 { 119 if (e->base_addr != PCIE_BASE_ADDR_UNMAPPED) { 120 memory_region_del_subregion(get_system_memory(), &e->mmio); 121 memory_region_destroy(&e->mmio); 122 e->base_addr = PCIE_BASE_ADDR_UNMAPPED; 123 } 124 } 125 126 void pcie_host_mmcfg_map(PCIExpressHost *e, hwaddr addr, 127 uint32_t size) 128 { 129 assert(!(size & (size - 1))); /* power of 2 */ 130 assert(size >= PCIE_MMCFG_SIZE_MIN); 131 assert(size <= PCIE_MMCFG_SIZE_MAX); 132 e->size = size; 133 memory_region_init_io(&e->mmio, &pcie_mmcfg_ops, e, "pcie-mmcfg", e->size); 134 e->base_addr = addr; 135 memory_region_add_subregion(get_system_memory(), e->base_addr, &e->mmio); 136 } 137 138 void pcie_host_mmcfg_update(PCIExpressHost *e, 139 int enable, 140 hwaddr addr, 141 uint32_t size) 142 { 143 pcie_host_mmcfg_unmap(e); 144 if (enable) { 145 pcie_host_mmcfg_map(e, addr, size); 146 } 147 } 148 149 static const TypeInfo pcie_host_type_info = { 150 .name = TYPE_PCIE_HOST_BRIDGE, 151 .parent = TYPE_PCI_HOST_BRIDGE, 152 .abstract = true, 153 .instance_size = sizeof(PCIExpressHost), 154 }; 155 156 static void pcie_host_register_types(void) 157 { 158 type_register_static(&pcie_host_type_info); 159 } 160 161 type_init(pcie_host_register_types) 162