1 /* 2 * pci_host.c 3 * 4 * Copyright (c) 2009 Isaku Yamahata <yamahata at valinux co jp> 5 * VA Linux Systems Japan K.K. 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 17 * You should have received a copy of the GNU General Public License along 18 * with this program; if not, see <http://www.gnu.org/licenses/>. 19 */ 20 21 #include "hw/pci/pci.h" 22 #include "hw/pci/pci_host.h" 23 24 /* debug PCI */ 25 //#define DEBUG_PCI 26 27 #ifdef DEBUG_PCI 28 #define PCI_DPRINTF(fmt, ...) \ 29 do { printf("pci_host_data: " fmt , ## __VA_ARGS__); } while (0) 30 #else 31 #define PCI_DPRINTF(fmt, ...) 32 #endif 33 34 /* 35 * PCI address 36 * bit 16 - 24: bus number 37 * bit 8 - 15: devfun number 38 * bit 0 - 7: offset in configuration space of a given pci device 39 */ 40 41 /* the helper function to get a PCIDevice* for a given pci address */ 42 static inline PCIDevice *pci_dev_find_by_addr(PCIBus *bus, uint32_t addr) 43 { 44 uint8_t bus_num = addr >> 16; 45 uint8_t devfn = addr >> 8; 46 47 return pci_find_device(bus, bus_num, devfn); 48 } 49 50 void pci_host_config_write_common(PCIDevice *pci_dev, uint32_t addr, 51 uint32_t limit, uint32_t val, uint32_t len) 52 { 53 assert(len <= 4); 54 pci_dev->config_write(pci_dev, addr, val, MIN(len, limit - addr)); 55 } 56 57 uint32_t pci_host_config_read_common(PCIDevice *pci_dev, uint32_t addr, 58 uint32_t limit, uint32_t len) 59 { 60 assert(len <= 4); 61 return pci_dev->config_read(pci_dev, addr, MIN(len, limit - addr)); 62 } 63 64 void pci_data_write(PCIBus *s, uint32_t addr, uint32_t val, int len) 65 { 66 PCIDevice *pci_dev = pci_dev_find_by_addr(s, addr); 67 uint32_t config_addr = addr & (PCI_CONFIG_SPACE_SIZE - 1); 68 69 if (!pci_dev) { 70 return; 71 } 72 73 PCI_DPRINTF("%s: %s: addr=%02" PRIx32 " val=%08" PRIx32 " len=%d\n", 74 __func__, pci_dev->name, config_addr, val, len); 75 pci_host_config_write_common(pci_dev, config_addr, PCI_CONFIG_SPACE_SIZE, 76 val, len); 77 } 78 79 uint32_t pci_data_read(PCIBus *s, uint32_t addr, int len) 80 { 81 PCIDevice *pci_dev = pci_dev_find_by_addr(s, addr); 82 uint32_t config_addr = addr & (PCI_CONFIG_SPACE_SIZE - 1); 83 uint32_t val; 84 85 if (!pci_dev) { 86 return ~0x0; 87 } 88 89 val = pci_host_config_read_common(pci_dev, config_addr, 90 PCI_CONFIG_SPACE_SIZE, len); 91 PCI_DPRINTF("%s: %s: addr=%02"PRIx32" val=%08"PRIx32" len=%d\n", 92 __func__, pci_dev->name, config_addr, val, len); 93 94 return val; 95 } 96 97 static void pci_host_config_write(void *opaque, hwaddr addr, 98 uint64_t val, unsigned len) 99 { 100 PCIHostState *s = opaque; 101 102 PCI_DPRINTF("%s addr " TARGET_FMT_plx " len %d val %"PRIx64"\n", 103 __func__, addr, len, val); 104 if (addr != 0 || len != 4) { 105 return; 106 } 107 s->config_reg = val; 108 } 109 110 static uint64_t pci_host_config_read(void *opaque, hwaddr addr, 111 unsigned len) 112 { 113 PCIHostState *s = opaque; 114 uint32_t val = s->config_reg; 115 116 PCI_DPRINTF("%s addr " TARGET_FMT_plx " len %d val %"PRIx32"\n", 117 __func__, addr, len, val); 118 return val; 119 } 120 121 static void pci_host_data_write(void *opaque, hwaddr addr, 122 uint64_t val, unsigned len) 123 { 124 PCIHostState *s = opaque; 125 PCI_DPRINTF("write addr " TARGET_FMT_plx " len %d val %x\n", 126 addr, len, (unsigned)val); 127 if (s->config_reg & (1u << 31)) 128 pci_data_write(s->bus, s->config_reg | (addr & 3), val, len); 129 } 130 131 static uint64_t pci_host_data_read(void *opaque, 132 hwaddr addr, unsigned len) 133 { 134 PCIHostState *s = opaque; 135 uint32_t val; 136 if (!(s->config_reg & (1 << 31))) 137 return 0xffffffff; 138 val = pci_data_read(s->bus, s->config_reg | (addr & 3), len); 139 PCI_DPRINTF("read addr " TARGET_FMT_plx " len %d val %x\n", 140 addr, len, val); 141 return val; 142 } 143 144 const MemoryRegionOps pci_host_conf_le_ops = { 145 .read = pci_host_config_read, 146 .write = pci_host_config_write, 147 .endianness = DEVICE_LITTLE_ENDIAN, 148 }; 149 150 const MemoryRegionOps pci_host_conf_be_ops = { 151 .read = pci_host_config_read, 152 .write = pci_host_config_write, 153 .endianness = DEVICE_BIG_ENDIAN, 154 }; 155 156 const MemoryRegionOps pci_host_data_le_ops = { 157 .read = pci_host_data_read, 158 .write = pci_host_data_write, 159 .endianness = DEVICE_LITTLE_ENDIAN, 160 }; 161 162 const MemoryRegionOps pci_host_data_be_ops = { 163 .read = pci_host_data_read, 164 .write = pci_host_data_write, 165 .endianness = DEVICE_BIG_ENDIAN, 166 }; 167 168 static const TypeInfo pci_host_type_info = { 169 .name = TYPE_PCI_HOST_BRIDGE, 170 .parent = TYPE_SYS_BUS_DEVICE, 171 .abstract = true, 172 .instance_size = sizeof(PCIHostState), 173 }; 174 175 static void pci_host_register_types(void) 176 { 177 type_register_static(&pci_host_type_info); 178 } 179 180 type_init(pci_host_register_types) 181