1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
|
/*
* QEMU PowerPC PowerNV Proxy PHB model
*
* Copyright (c) 2022, IBM Corporation.
*
* This code is licensed under the GPL version 2 or later. See the
* COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "qapi/visitor.h"
#include "qapi/error.h"
#include "hw/pci-host/pnv_phb.h"
#include "hw/pci-host/pnv_phb3.h"
#include "hw/pci-host/pnv_phb4.h"
#include "hw/ppc/pnv.h"
#include "hw/qdev-properties.h"
#include "qom/object.h"
#include "sysemu/sysemu.h"
/*
* Set the QOM parent of an object child. If the device state
* associated with the child has an id, use it as QOM id. Otherwise
* use object_typename[index] as QOM id.
*/
static void pnv_parent_qom_fixup(Object *parent, Object *child, int index)
{
g_autofree char *default_id =
g_strdup_printf("%s[%d]", object_get_typename(child), index);
const char *dev_id = DEVICE(child)->id;
if (child->parent == parent) {
return;
}
object_ref(child);
object_unparent(child);
object_property_add_child(parent, dev_id ? dev_id : default_id, child);
object_unref(child);
}
static void pnv_parent_bus_fixup(DeviceState *parent, DeviceState *child,
Error **errp)
{
BusState *parent_bus = qdev_get_parent_bus(parent);
if (!qdev_set_parent_bus(child, parent_bus, errp)) {
return;
}
}
/*
* Attach a root port device.
*
* 'index' will be used both as a PCIE slot value and to calculate
* QOM id. 'chip_id' is going to be used as PCIE chassis for the
* root port.
*/
static void pnv_phb_attach_root_port(PCIHostState *pci)
{
PCIDevice *root = pci_new(PCI_DEVFN(0, 0), TYPE_PNV_PHB_ROOT_PORT);
const char *dev_id = DEVICE(root)->id;
g_autofree char *default_id = NULL;
int index;
index = object_property_get_int(OBJECT(pci->bus), "phb-id", &error_fatal);
default_id = g_strdup_printf("%s[%d]", TYPE_PNV_PHB_ROOT_PORT, index);
object_property_add_child(OBJECT(pci->bus), dev_id ? dev_id : default_id,
OBJECT(root));
pci_realize_and_unref(root, pci->bus, &error_fatal);
}
/*
* User created devices won't have the initial setup that default
* devices have. This setup consists of assigning a parent device
* (chip for PHB3, PEC for PHB4/5) that will be the QOM/bus parent
* of the PHB.
*/
static bool pnv_phb_user_device_init(PnvPHB *phb, Error **errp)
{
PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine());
PnvChip *chip = pnv_get_chip(pnv, phb->chip_id);
Object *parent = NULL;
if (!chip) {
error_setg(errp, "invalid chip id: %d", phb->chip_id);
return false;
}
parent = pnv_chip_add_phb(chip, phb, errp);
if (!parent) {
return false;
}
/*
* Reparent user created devices to the chip to build
* correctly the device tree. pnv_xscom_dt() needs every
* PHB to be a child of the chip to build the DT correctly.
*/
pnv_parent_qom_fixup(parent, OBJECT(phb), phb->phb_id);
pnv_parent_bus_fixup(DEVICE(chip), DEVICE(phb), errp);
return true;
}
static void pnv_phb_realize(DeviceState *dev, Error **errp)
{
PnvPHB *phb = PNV_PHB(dev);
PCIHostState *pci = PCI_HOST_BRIDGE(dev);
g_autofree char *phb_typename = NULL;
if (!phb->version) {
error_setg(errp, "version not specified");
return;
}
switch (phb->version) {
case 3:
phb_typename = g_strdup(TYPE_PNV_PHB3);
break;
case 4:
phb_typename = g_strdup(TYPE_PNV_PHB4);
break;
case 5:
phb_typename = g_strdup(TYPE_PNV_PHB5);
break;
default:
g_assert_not_reached();
}
phb->backend = object_new(phb_typename);
object_property_add_child(OBJECT(dev), "phb-backend", phb->backend);
/* Passthrough child device properties to the proxy device */
object_property_set_uint(phb->backend, "index", phb->phb_id, errp);
object_property_set_uint(phb->backend, "chip-id", phb->chip_id, errp);
object_property_set_link(phb->backend, "phb-base", OBJECT(phb), errp);
/*
* Handle user created devices. User devices will not have a
* pointer to a chip (PHB3) and a PEC (PHB4/5).
*/
if (!phb->chip && !phb->pec) {
if (!pnv_phb_user_device_init(phb, errp)) {
return;
}
}
if (phb->version == 3) {
object_property_set_link(phb->backend, "chip",
OBJECT(phb->chip), errp);
} else {
object_property_set_link(phb->backend, "pec", OBJECT(phb->pec), errp);
}
if (!qdev_realize(DEVICE(phb->backend), NULL, errp)) {
return;
}
if (phb->version == 3) {
pnv_phb3_bus_init(dev, PNV_PHB3(phb->backend));
} else {
pnv_phb4_bus_init(dev, PNV_PHB4(phb->backend));
}
if (!defaults_enabled()) {
return;
}
pnv_phb_attach_root_port(pci);
}
static const char *pnv_phb_root_bus_path(PCIHostState *host_bridge,
PCIBus *rootbus)
{
PnvPHB *phb = PNV_PHB(host_bridge);
snprintf(phb->bus_path, sizeof(phb->bus_path), "00%02x:%02x",
phb->chip_id, phb->phb_id);
return phb->bus_path;
}
static Property pnv_phb_properties[] = {
DEFINE_PROP_UINT32("index", PnvPHB, phb_id, 0),
DEFINE_PROP_UINT32("chip-id", PnvPHB, chip_id, 0),
DEFINE_PROP_UINT32("version", PnvPHB, version, 0),
DEFINE_PROP_LINK("chip", PnvPHB, chip, TYPE_PNV_CHIP, PnvChip *),
DEFINE_PROP_LINK("pec", PnvPHB, pec, TYPE_PNV_PHB4_PEC,
PnvPhb4PecState *),
DEFINE_PROP_END_OF_LIST(),
};
static void pnv_phb_class_init(ObjectClass *klass, void *data)
{
PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
hc->root_bus_path = pnv_phb_root_bus_path;
dc->realize = pnv_phb_realize;
device_class_set_props(dc, pnv_phb_properties);
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
dc->user_creatable = true;
}
static void pnv_phb_root_port_reset(DeviceState *dev)
{
PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(dev);
PnvPHBRootPort *phb_rp = PNV_PHB_ROOT_PORT(dev);
PCIDevice *d = PCI_DEVICE(dev);
uint8_t *conf = d->config;
rpc->parent_reset(dev);
if (phb_rp->version == 3) {
return;
}
/* PHB4 and later requires these extra reset steps */
pci_byte_test_and_set_mask(conf + PCI_IO_BASE,
PCI_IO_RANGE_MASK & 0xff);
pci_byte_test_and_clear_mask(conf + PCI_IO_LIMIT,
PCI_IO_RANGE_MASK & 0xff);
pci_set_word(conf + PCI_MEMORY_BASE, 0);
pci_set_word(conf + PCI_MEMORY_LIMIT, 0xfff0);
pci_set_word(conf + PCI_PREF_MEMORY_BASE, 0x1);
pci_set_word(conf + PCI_PREF_MEMORY_LIMIT, 0xfff1);
pci_set_long(conf + PCI_PREF_BASE_UPPER32, 0x1); /* Hack */
pci_set_long(conf + PCI_PREF_LIMIT_UPPER32, 0xffffffff);
pci_config_set_interrupt_pin(conf, 0);
}
static void pnv_phb_root_port_realize(DeviceState *dev, Error **errp)
{
PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(dev);
PnvPHBRootPort *phb_rp = PNV_PHB_ROOT_PORT(dev);
PCIBus *bus = PCI_BUS(qdev_get_parent_bus(dev));
PCIDevice *pci = PCI_DEVICE(dev);
uint16_t device_id = 0;
Error *local_err = NULL;
int chip_id, index;
chip_id = object_property_get_int(OBJECT(bus), "chip-id", &error_fatal);
index = object_property_get_int(OBJECT(bus), "phb-id", &error_fatal);
/* Set unique chassis/slot values for the root port */
qdev_prop_set_uint8(dev, "chassis", chip_id);
qdev_prop_set_uint16(dev, "slot", index);
rpc->parent_realize(dev, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
switch (phb_rp->version) {
case 3:
device_id = PNV_PHB3_DEVICE_ID;
break;
case 4:
device_id = PNV_PHB4_DEVICE_ID;
break;
case 5:
device_id = PNV_PHB5_DEVICE_ID;
break;
default:
g_assert_not_reached();
}
pci_config_set_device_id(pci->config, device_id);
pci_config_set_interrupt_pin(pci->config, 0);
}
static Property pnv_phb_root_port_properties[] = {
DEFINE_PROP_UINT32("version", PnvPHBRootPort, version, 0),
DEFINE_PROP_END_OF_LIST(),
};
static void pnv_phb_root_port_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
PCIERootPortClass *rpc = PCIE_ROOT_PORT_CLASS(klass);
dc->desc = "IBM PHB PCIE Root Port";
device_class_set_props(dc, pnv_phb_root_port_properties);
device_class_set_parent_realize(dc, pnv_phb_root_port_realize,
&rpc->parent_realize);
device_class_set_parent_reset(dc, pnv_phb_root_port_reset,
&rpc->parent_reset);
dc->reset = &pnv_phb_root_port_reset;
dc->user_creatable = true;
k->vendor_id = PCI_VENDOR_ID_IBM;
/* device_id will be written during realize() */
k->device_id = 0;
k->revision = 0;
rpc->exp_offset = 0x48;
rpc->aer_offset = 0x100;
}
static const TypeInfo pnv_phb_type_info = {
.name = TYPE_PNV_PHB,
.parent = TYPE_PCIE_HOST_BRIDGE,
.instance_size = sizeof(PnvPHB),
.class_init = pnv_phb_class_init,
};
static const TypeInfo pnv_phb_root_port_info = {
.name = TYPE_PNV_PHB_ROOT_PORT,
.parent = TYPE_PCIE_ROOT_PORT,
.instance_size = sizeof(PnvPHBRootPort),
.class_init = pnv_phb_root_port_class_init,
};
static void pnv_phb_register_types(void)
{
type_register_static(&pnv_phb_type_info);
type_register_static(&pnv_phb_root_port_info);
}
type_init(pnv_phb_register_types)
|