xref: /openbmc/qemu/hw/pci/pci_host.c (revision d73abd6d)
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 #include "hw/pci/pci_bus.h"
24 #include "trace.h"
25 
26 /* debug PCI */
27 //#define DEBUG_PCI
28 
29 #ifdef DEBUG_PCI
30 #define PCI_DPRINTF(fmt, ...) \
31 do { printf("pci_host_data: " fmt , ## __VA_ARGS__); } while (0)
32 #else
33 #define PCI_DPRINTF(fmt, ...)
34 #endif
35 
36 /*
37  * PCI address
38  * bit 16 - 24: bus number
39  * bit  8 - 15: devfun number
40  * bit  0 -  7: offset in configuration space of a given pci device
41  */
42 
43 /* the helper function to get a PCIDevice* for a given pci address */
44 static inline PCIDevice *pci_dev_find_by_addr(PCIBus *bus, uint32_t addr)
45 {
46     uint8_t bus_num = addr >> 16;
47     uint8_t devfn = addr >> 8;
48 
49     return pci_find_device(bus, bus_num, devfn);
50 }
51 
52 void pci_host_config_write_common(PCIDevice *pci_dev, uint32_t addr,
53                                   uint32_t limit, uint32_t val, uint32_t len)
54 {
55     assert(len <= 4);
56     /* non-zero functions are only exposed when function 0 is present,
57      * allowing direct removal of unexposed functions.
58      */
59     if (pci_dev->qdev.hotplugged && !pci_get_function_0(pci_dev)) {
60         return;
61     }
62 
63     trace_pci_cfg_write(pci_dev->name, PCI_SLOT(pci_dev->devfn),
64                         PCI_FUNC(pci_dev->devfn), addr, val);
65     pci_dev->config_write(pci_dev, addr, val, MIN(len, limit - addr));
66 }
67 
68 uint32_t pci_host_config_read_common(PCIDevice *pci_dev, uint32_t addr,
69                                      uint32_t limit, uint32_t len)
70 {
71     uint32_t ret;
72 
73     assert(len <= 4);
74     /* non-zero functions are only exposed when function 0 is present,
75      * allowing direct removal of unexposed functions.
76      */
77     if (pci_dev->qdev.hotplugged && !pci_get_function_0(pci_dev)) {
78         return ~0x0;
79     }
80 
81     ret = pci_dev->config_read(pci_dev, addr, MIN(len, limit - addr));
82     trace_pci_cfg_read(pci_dev->name, PCI_SLOT(pci_dev->devfn),
83                        PCI_FUNC(pci_dev->devfn), addr, ret);
84 
85     return ret;
86 }
87 
88 void pci_data_write(PCIBus *s, uint32_t addr, uint32_t val, int len)
89 {
90     PCIDevice *pci_dev = pci_dev_find_by_addr(s, addr);
91     uint32_t config_addr = addr & (PCI_CONFIG_SPACE_SIZE - 1);
92 
93     if (!pci_dev) {
94         return;
95     }
96 
97     PCI_DPRINTF("%s: %s: addr=%02" PRIx32 " val=%08" PRIx32 " len=%d\n",
98                 __func__, pci_dev->name, config_addr, val, len);
99     pci_host_config_write_common(pci_dev, config_addr, PCI_CONFIG_SPACE_SIZE,
100                                  val, len);
101 }
102 
103 uint32_t pci_data_read(PCIBus *s, uint32_t addr, int len)
104 {
105     PCIDevice *pci_dev = pci_dev_find_by_addr(s, addr);
106     uint32_t config_addr = addr & (PCI_CONFIG_SPACE_SIZE - 1);
107     uint32_t val;
108 
109     if (!pci_dev) {
110         return ~0x0;
111     }
112 
113     val = pci_host_config_read_common(pci_dev, config_addr,
114                                       PCI_CONFIG_SPACE_SIZE, len);
115     PCI_DPRINTF("%s: %s: addr=%02"PRIx32" val=%08"PRIx32" len=%d\n",
116                 __func__, pci_dev->name, config_addr, val, len);
117 
118     return val;
119 }
120 
121 static void pci_host_config_write(void *opaque, hwaddr addr,
122                                   uint64_t val, unsigned len)
123 {
124     PCIHostState *s = opaque;
125 
126     PCI_DPRINTF("%s addr " TARGET_FMT_plx " len %d val %"PRIx64"\n",
127                 __func__, addr, len, val);
128     if (addr != 0 || len != 4) {
129         return;
130     }
131     s->config_reg = val;
132 }
133 
134 static uint64_t pci_host_config_read(void *opaque, hwaddr addr,
135                                      unsigned len)
136 {
137     PCIHostState *s = opaque;
138     uint32_t val = s->config_reg;
139 
140     PCI_DPRINTF("%s addr " TARGET_FMT_plx " len %d val %"PRIx32"\n",
141                 __func__, addr, len, val);
142     return val;
143 }
144 
145 static void pci_host_data_write(void *opaque, hwaddr addr,
146                                 uint64_t val, unsigned len)
147 {
148     PCIHostState *s = opaque;
149     PCI_DPRINTF("write addr " TARGET_FMT_plx " len %d val %x\n",
150                 addr, len, (unsigned)val);
151     if (s->config_reg & (1u << 31))
152         pci_data_write(s->bus, s->config_reg | (addr & 3), val, len);
153 }
154 
155 static uint64_t pci_host_data_read(void *opaque,
156                                    hwaddr addr, unsigned len)
157 {
158     PCIHostState *s = opaque;
159     uint32_t val;
160     if (!(s->config_reg & (1U << 31))) {
161         return 0xffffffff;
162     }
163     val = pci_data_read(s->bus, s->config_reg | (addr & 3), len);
164     PCI_DPRINTF("read addr " TARGET_FMT_plx " len %d val %x\n",
165                 addr, len, val);
166     return val;
167 }
168 
169 const MemoryRegionOps pci_host_conf_le_ops = {
170     .read = pci_host_config_read,
171     .write = pci_host_config_write,
172     .endianness = DEVICE_LITTLE_ENDIAN,
173 };
174 
175 const MemoryRegionOps pci_host_conf_be_ops = {
176     .read = pci_host_config_read,
177     .write = pci_host_config_write,
178     .endianness = DEVICE_BIG_ENDIAN,
179 };
180 
181 const MemoryRegionOps pci_host_data_le_ops = {
182     .read = pci_host_data_read,
183     .write = pci_host_data_write,
184     .endianness = DEVICE_LITTLE_ENDIAN,
185 };
186 
187 const MemoryRegionOps pci_host_data_be_ops = {
188     .read = pci_host_data_read,
189     .write = pci_host_data_write,
190     .endianness = DEVICE_BIG_ENDIAN,
191 };
192 
193 static const TypeInfo pci_host_type_info = {
194     .name = TYPE_PCI_HOST_BRIDGE,
195     .parent = TYPE_SYS_BUS_DEVICE,
196     .abstract = true,
197     .class_size = sizeof(PCIHostBridgeClass),
198     .instance_size = sizeof(PCIHostState),
199 };
200 
201 static void pci_host_register_types(void)
202 {
203     type_register_static(&pci_host_type_info);
204 }
205 
206 type_init(pci_host_register_types)
207