xref: /openbmc/qemu/hw/pci-host/aspeed_pcie.c (revision 829c97b0c72eb2eb387ec2d627d1604d1662e393)
1 /*
2  * ASPEED PCIe Host Controller
3  *
4  * Copyright (C) 2025 ASPEED Technology Inc.
5  * Copyright (c) 2022 Cédric Le Goater <clg@kaod.org>
6  *
7  * Jamin Lin <jamin_lin@aspeedtech.com>
8  *
9  * SPDX-License-Identifier: GPL-2.0-or-later
10  *
11  * This file is based on Cédric Le Goater's patch:
12  * "pci: Add Aspeed host bridge (WIP)"
13  * https://github.com/legoater/qemu/commit/d1b97b0c7844219d847122410dc189854f9d26df
14  *
15  * Modifications have been made to support the Aspeed AST2600 and AST2700
16  * platforms.
17  */
18 
19 #include "qemu/osdep.h"
20 #include "qemu/log.h"
21 #include "qapi/error.h"
22 #include "hw/qdev-properties.h"
23 #include "hw/registerfields.h"
24 #include "hw/irq.h"
25 #include "hw/pci/pci_host.h"
26 #include "hw/pci-host/aspeed_pcie.h"
27 #include "hw/pci/msi.h"
28 #include "trace.h"
29 
30 /*
31  * PCIe Root
32  */
33 
34 static void aspeed_pcie_root_class_init(ObjectClass *klass, const void *data)
35 {
36     PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
37     DeviceClass *dc = DEVICE_CLASS(klass);
38 
39     set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
40     dc->desc = "ASPEED PCIe Host Bridge";
41     k->vendor_id = PCI_VENDOR_ID_ASPEED;
42     k->device_id = 0x1150;
43     k->class_id = PCI_CLASS_BRIDGE_HOST;
44     k->revision = 0;
45 
46     /*
47      * PCI-facing part of the host bridge,
48      * not usable without the host-facing part
49      */
50     dc->user_creatable = false;
51 }
52 
53 static const TypeInfo aspeed_pcie_root_info = {
54     .name = TYPE_ASPEED_PCIE_ROOT,
55     .parent = TYPE_PCI_DEVICE,
56     .instance_size = sizeof(AspeedPCIERootState),
57     .class_init = aspeed_pcie_root_class_init,
58     .interfaces = (const InterfaceInfo[]) {
59         { INTERFACE_CONVENTIONAL_PCI_DEVICE },
60         { },
61     },
62 };
63 
64 /*
65  * PCIe Root Complex (RC)
66  */
67 
68 static void aspeed_pcie_rc_set_irq(void *opaque, int irq, int level)
69 {
70     AspeedPCIERcState *rc = (AspeedPCIERcState *) opaque;
71     AspeedPCIECfgState *cfg =
72         container_of(rc, AspeedPCIECfgState, rc);
73     AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_GET_CLASS(cfg);
74     const AspeedPCIERcRegs *rc_regs;
75     bool intx;
76 
77     assert(irq < PCI_NUM_PINS);
78 
79     rc_regs = &apc->reg_map->rc;
80 
81     if (level) {
82         cfg->regs[rc_regs->int_sts_reg] |= BIT(irq);
83     } else {
84         cfg->regs[rc_regs->int_sts_reg] &= ~BIT(irq);
85     }
86 
87     intx = !!(cfg->regs[rc_regs->int_sts_reg] & cfg->regs[rc_regs->int_en_reg]);
88     trace_aspeed_pcie_rc_intx_set_irq(cfg->id, irq, intx);
89     qemu_set_irq(rc->irq, intx);
90 }
91 
92 static int aspeed_pcie_rc_map_irq(PCIDevice *pci_dev, int irq_num)
93 {
94     return irq_num % PCI_NUM_PINS;
95 }
96 
97 static void aspeed_pcie_rc_realize(DeviceState *dev, Error **errp)
98 {
99     PCIExpressHost *pex = PCIE_HOST_BRIDGE(dev);
100     AspeedPCIERcState *rc = ASPEED_PCIE_RC(dev);
101     AspeedPCIECfgState *cfg =
102            container_of(rc, AspeedPCIECfgState, rc);
103     PCIHostState *pci = PCI_HOST_BRIDGE(dev);
104     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
105     g_autofree char *name;
106 
107     /* PCI configuration space */
108     pcie_host_mmcfg_init(pex, PCIE_MMCFG_SIZE_MAX);
109     sysbus_init_mmio(sbd, &pex->mmio);
110 
111     /* MMIO and IO region */
112     memory_region_init(&rc->mmio, OBJECT(rc), "mmio", UINT64_MAX);
113     memory_region_init(&rc->io, OBJECT(rc), "io", 0x10000);
114 
115     name = g_strdup_printf("pcie.%d.mmio_window", cfg->id);
116     memory_region_init_io(&rc->mmio_window, OBJECT(rc), &unassigned_io_ops,
117                           OBJECT(rc), name, UINT64_MAX);
118     name = g_strdup_printf("pcie.%d.ioport_window", cfg->id);
119     memory_region_init_io(&rc->io_window, OBJECT(rc), &unassigned_io_ops,
120                           OBJECT(rc), name, 0x10000);
121 
122     memory_region_add_subregion(&rc->mmio_window, 0, &rc->mmio);
123     memory_region_add_subregion(&rc->io_window, 0, &rc->io);
124     sysbus_init_mmio(sbd, &rc->mmio_window);
125     sysbus_init_mmio(sbd, &rc->io_window);
126 
127     sysbus_init_irq(sbd, &rc->irq);
128     pci->bus = pci_register_root_bus(dev, NULL, aspeed_pcie_rc_set_irq,
129                                      aspeed_pcie_rc_map_irq, rc, &rc->mmio,
130                                      &rc->io, 0, 4, TYPE_PCIE_BUS);
131     pci->bus->flags |= PCI_BUS_EXTENDED_CONFIG_SPACE;
132 
133     qdev_realize(DEVICE(&rc->root), BUS(pci->bus), &error_fatal);
134 }
135 
136 static const char *aspeed_pcie_rc_root_bus_path(PCIHostState *host_bridge,
137                                                 PCIBus *rootbus)
138 {
139     AspeedPCIERcState *s = ASPEED_PCIE_RC(host_bridge);
140 
141     snprintf(s->name, sizeof(s->name), "0000:%02x", s->bus_nr);
142 
143     return s->name;
144 }
145 
146 static void aspeed_pcie_rc_instance_init(Object *obj)
147 {
148     AspeedPCIERcState *s = ASPEED_PCIE_RC(obj);
149     AspeedPCIERootState *root = &s->root;
150 
151     object_initialize_child(obj, "root", root, TYPE_ASPEED_PCIE_ROOT);
152     qdev_prop_set_int32(DEVICE(root), "addr", PCI_DEVFN(0, 0));
153     qdev_prop_set_bit(DEVICE(root), "multifunction", false);
154 }
155 
156 static const Property aspeed_pcie_rc_props[] = {
157     DEFINE_PROP_UINT32("bus-nr", AspeedPCIERcState, bus_nr, 0),
158 };
159 
160 static void aspeed_pcie_rc_class_init(ObjectClass *klass, const void *data)
161 {
162     PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass);
163     DeviceClass *dc = DEVICE_CLASS(klass);
164 
165     dc->desc = "ASPEED PCIe RC";
166     dc->realize = aspeed_pcie_rc_realize;
167     dc->fw_name = "pci";
168     set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
169 
170     hc->root_bus_path = aspeed_pcie_rc_root_bus_path;
171     device_class_set_props(dc, aspeed_pcie_rc_props);
172 
173     msi_nonbroken = true;
174 }
175 
176 static const TypeInfo aspeed_pcie_rc_info = {
177     .name = TYPE_ASPEED_PCIE_RC,
178     .parent = TYPE_PCIE_HOST_BRIDGE,
179     .instance_size = sizeof(AspeedPCIERcState),
180     .instance_init = aspeed_pcie_rc_instance_init,
181     .class_init = aspeed_pcie_rc_class_init,
182 };
183 
184 /*
185  * PCIe Config
186  *
187  * AHB to PCIe Bus Bridge (H2X)
188  *
189  * On the AST2600:
190  * NOTE: rc_l is not supported by this model.
191  * - Registers 0x00 - 0x7F are shared by both PCIe0 (rc_l) and PCIe1 (rc_h).
192  * - Registers 0x80 - 0xBF are specific to PCIe0.
193  * - Registers 0xC0 - 0xFF are specific to PCIe1.
194  */
195 
196 /* AST2600 */
197 REG32(H2X_CTRL,             0x00)
198     FIELD(H2X_CTRL, CLEAR_RX, 4, 1)
199 REG32(H2X_TX_CLEAR,         0x08)
200     FIELD(H2X_TX_CLEAR, IDLE, 0, 1)
201 REG32(H2X_RDATA,            0x0C)
202 REG32(H2X_TX_DESC0,         0x10)
203 REG32(H2X_TX_DESC1,         0x14)
204 REG32(H2X_TX_DESC2,         0x18)
205 REG32(H2X_TX_DESC3,         0x1C)
206 REG32(H2X_TX_DATA,          0x20)
207 REG32(H2X_TX_STS,           0x24)
208     FIELD(H2X_TX_STS, IDLE, 31, 1)
209     FIELD(H2X_TX_STS, RC_L_TX_COMP, 24, 1)
210     FIELD(H2X_TX_STS, RC_H_TX_COMP, 25, 1)
211     FIELD(H2X_TX_STS, TRIG, 0, 1)
212 REG32(H2X_RC_H_CTRL,        0xC0)
213 REG32(H2X_RC_H_INT_EN,      0xC4)
214 REG32(H2X_RC_H_INT_STS,     0xC8)
215     SHARED_FIELD(H2X_RC_INT_INTDONE, 4, 1)
216     SHARED_FIELD(H2X_RC_INT_INTX, 0, 4)
217 REG32(H2X_RC_H_RDATA,       0xCC)
218 
219 #define TLP_FMTTYPE_CFGRD0  0x04 /* Configuration Read  Type 0 */
220 #define TLP_FMTTYPE_CFGWR0  0x44 /* Configuration Write Type 0 */
221 #define TLP_FMTTYPE_CFGRD1  0x05 /* Configuration Read  Type 1 */
222 #define TLP_FMTTYPE_CFGWR1  0x45 /* Configuration Write Type 1 */
223 
224 #define PCIE_CFG_FMTTYPE_MASK(x) (((x) >> 24) & 0xff)
225 #define PCIE_CFG_BYTE_EN(x) ((x) & 0xf)
226 
227 static const AspeedPCIERegMap aspeed_regmap = {
228     .rc = {
229         .int_en_reg     = R_H2X_RC_H_INT_EN,
230         .int_sts_reg    = R_H2X_RC_H_INT_STS,
231     },
232 };
233 
234 static uint64_t aspeed_pcie_cfg_read(void *opaque, hwaddr addr,
235                                      unsigned int size)
236 {
237     AspeedPCIECfgState *s = ASPEED_PCIE_CFG(opaque);
238     uint32_t reg = addr >> 2;
239     uint32_t value = 0;
240 
241     value = s->regs[reg];
242 
243     trace_aspeed_pcie_cfg_read(s->id, addr, value);
244 
245     return value;
246 }
247 
248 static void aspeed_pcie_cfg_translate_write(uint8_t byte_en, uint32_t *addr,
249                                             uint64_t *val, int *len)
250 {
251     uint64_t packed_val = 0;
252     int first_bit = -1;
253     int index = 0;
254     int i;
255 
256     *len = ctpop8(byte_en);
257 
258     if (*len == 0 || *len > 4) {
259         goto err;
260     }
261 
262     /* Special case: full 4-byte write must be 4-byte aligned */
263     if (byte_en == 0x0f) {
264         if (*addr % 4 != 0) {
265             goto err;
266         }
267         *val = *val & 0xffffffff;
268         return;
269     }
270 
271     for (i = 0; i < 4; i++) {
272         if (byte_en & (1 << i)) {
273             if (first_bit < 0) {
274                 first_bit = i;
275             }
276             packed_val |= ((*val >> (i * 8)) & 0xff) << (index * 8);
277             index++;
278         }
279     }
280 
281     *addr += first_bit;
282     *val = packed_val;
283 
284     return;
285 
286 err:
287     qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid byte enable: 0x%x\n",
288                   __func__, byte_en);
289 }
290 
291 static void aspeed_pcie_cfg_readwrite(AspeedPCIECfgState *s,
292                                       const AspeedPCIECfgTxDesc *desc)
293 {
294     AspeedPCIERcState *rc = &s->rc;
295     PCIHostState *pci;
296     uint32_t cfg_addr;
297     PCIDevice *pdev;
298     uint32_t offset;
299     uint8_t byte_en;
300     bool is_write;
301     uint8_t devfn;
302     uint64_t val;
303     uint8_t bus;
304     int len;
305 
306     val = ~0;
307     is_write = !!(desc->desc0 & BIT(30));
308     cfg_addr = desc->desc2;
309 
310     bus = (cfg_addr >> 24) & 0xff;
311     devfn  = (cfg_addr >> 16) & 0xff;
312     offset = cfg_addr & 0xffc;
313 
314     pci = PCI_HOST_BRIDGE(rc);
315 
316     /*
317      * On the AST2600, the RC_H bus number ranges from 0x80 to 0xFF, and its
318      * root port uses bus number 0x80 instead of the standard 0x00. To locate
319      * the device at root port 0, remap bus number 0x80 to 0x00 so that the
320      * PCI subsystem can correctly discover the devices.
321      */
322     if (bus == rc->bus_nr) {
323         bus = 0;
324     }
325 
326     pdev = pci_find_device(pci->bus, bus, devfn);
327     if (!pdev) {
328         s->regs[desc->rdata_reg] = ~0;
329         goto out;
330     }
331 
332     switch (PCIE_CFG_FMTTYPE_MASK(desc->desc0)) {
333     case TLP_FMTTYPE_CFGWR0:
334     case TLP_FMTTYPE_CFGWR1:
335         byte_en = PCIE_CFG_BYTE_EN(desc->desc1);
336         val = desc->wdata;
337         aspeed_pcie_cfg_translate_write(byte_en, &offset, &val, &len);
338         pci_host_config_write_common(pdev, offset, pci_config_size(pdev),
339                                      val, len);
340         break;
341     case TLP_FMTTYPE_CFGRD0:
342     case TLP_FMTTYPE_CFGRD1:
343         val = pci_host_config_read_common(pdev, offset,
344                                           pci_config_size(pdev), 4);
345         s->regs[desc->rdata_reg] = val;
346         break;
347     default:
348         qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid CFG type. DESC0=0x%x\n",
349                       __func__, desc->desc0);
350     }
351 
352 out:
353     trace_aspeed_pcie_cfg_rw(s->id, is_write ?  "write" : "read", bus, devfn,
354                              cfg_addr, val);
355 }
356 
357 static void aspeed_pcie_cfg_write(void *opaque, hwaddr addr, uint64_t data,
358                                   unsigned int size)
359 {
360     AspeedPCIECfgState *s = ASPEED_PCIE_CFG(opaque);
361     AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_GET_CLASS(s);
362     AspeedPCIECfgTxDesc desc;
363     uint32_t reg = addr >> 2;
364     uint32_t rc_reg;
365 
366     trace_aspeed_pcie_cfg_write(s->id, addr, data);
367 
368     switch (reg) {
369     case R_H2X_CTRL:
370         if (data & R_H2X_CTRL_CLEAR_RX_MASK) {
371             s->regs[R_H2X_RDATA] = ~0;
372         }
373         break;
374     case R_H2X_TX_CLEAR:
375         if (data & R_H2X_TX_CLEAR_IDLE_MASK) {
376             s->regs[R_H2X_TX_STS] &= ~R_H2X_TX_STS_IDLE_MASK;
377         }
378         break;
379     case R_H2X_TX_STS:
380         if (data & R_H2X_TX_STS_TRIG_MASK) {
381             desc.desc0 = s->regs[R_H2X_TX_DESC0];
382             desc.desc1 = s->regs[R_H2X_TX_DESC1];
383             desc.desc2 = s->regs[R_H2X_TX_DESC2];
384             desc.desc3 = s->regs[R_H2X_TX_DESC3];
385             desc.wdata = s->regs[R_H2X_TX_DATA];
386             desc.rdata_reg = R_H2X_RC_H_RDATA;
387             aspeed_pcie_cfg_readwrite(s, &desc);
388             rc_reg = apc->reg_map->rc.int_sts_reg;
389             s->regs[rc_reg] |= H2X_RC_INT_INTDONE_MASK;
390             s->regs[R_H2X_TX_STS] |=
391                 BIT(R_H2X_TX_STS_RC_H_TX_COMP_SHIFT);
392             s->regs[R_H2X_TX_STS] |= R_H2X_TX_STS_IDLE_MASK;
393         }
394         break;
395     /* preserve INTx status */
396     case R_H2X_RC_H_INT_STS:
397         if (data & H2X_RC_INT_INTDONE_MASK) {
398             s->regs[R_H2X_TX_STS] &= ~R_H2X_TX_STS_RC_H_TX_COMP_MASK;
399         }
400         s->regs[reg] &= ~data | H2X_RC_INT_INTX_MASK;
401         break;
402     default:
403         s->regs[reg] = data;
404         break;
405     }
406 }
407 
408 static const MemoryRegionOps aspeed_pcie_cfg_ops = {
409     .read = aspeed_pcie_cfg_read,
410     .write = aspeed_pcie_cfg_write,
411     .endianness = DEVICE_LITTLE_ENDIAN,
412     .valid = {
413         .min_access_size = 1,
414         .max_access_size = 4,
415     },
416 };
417 
418 static void aspeed_pcie_cfg_instance_init(Object *obj)
419 {
420     AspeedPCIECfgState *s = ASPEED_PCIE_CFG(obj);
421 
422     object_initialize_child(obj, "rc", &s->rc, TYPE_ASPEED_PCIE_RC);
423 
424     return;
425 }
426 
427 static void aspeed_pcie_cfg_reset(DeviceState *dev)
428 {
429     AspeedPCIECfgState *s = ASPEED_PCIE_CFG(dev);
430     AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_GET_CLASS(s);
431 
432     memset(s->regs, 0, apc->nr_regs << 2);
433 }
434 
435 static void aspeed_pcie_cfg_realize(DeviceState *dev, Error **errp)
436 {
437     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
438     AspeedPCIECfgState *s = ASPEED_PCIE_CFG(dev);
439     AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_GET_CLASS(s);
440     g_autofree char *name;
441 
442     s->regs = g_new(uint32_t, apc->nr_regs);
443     name = g_strdup_printf(TYPE_ASPEED_PCIE_CFG ".regs.%d", s->id);
444     memory_region_init_io(&s->mmio, OBJECT(s), apc->reg_ops, s, name,
445                           apc->nr_regs << 2);
446     sysbus_init_mmio(sbd, &s->mmio);
447 
448     object_property_set_int(OBJECT(&s->rc), "bus-nr",
449                             apc->rc_bus_nr,
450                             &error_abort);
451     if (!sysbus_realize(SYS_BUS_DEVICE(&s->rc), errp)) {
452         return;
453     }
454 }
455 
456 static void aspeed_pcie_cfg_unrealize(DeviceState *dev)
457 {
458     AspeedPCIECfgState *s = ASPEED_PCIE_CFG(dev);
459 
460     g_free(s->regs);
461     s->regs = NULL;
462 }
463 
464 static const Property aspeed_pcie_cfg_props[] = {
465     DEFINE_PROP_UINT32("id", AspeedPCIECfgState, id, 0),
466 };
467 
468 static void aspeed_pcie_cfg_class_init(ObjectClass *klass, const void *data)
469 {
470     DeviceClass *dc = DEVICE_CLASS(klass);
471     AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_CLASS(klass);
472 
473     dc->desc = "ASPEED PCIe Config";
474     dc->realize = aspeed_pcie_cfg_realize;
475     dc->unrealize = aspeed_pcie_cfg_unrealize;
476     device_class_set_legacy_reset(dc, aspeed_pcie_cfg_reset);
477     device_class_set_props(dc, aspeed_pcie_cfg_props);
478 
479     apc->reg_ops = &aspeed_pcie_cfg_ops;
480     apc->reg_map = &aspeed_regmap;
481     apc->nr_regs = 0x100 >> 2;
482     apc->rc_bus_nr = 0x80;
483 }
484 
485 static const TypeInfo aspeed_pcie_cfg_info = {
486     .name       = TYPE_ASPEED_PCIE_CFG,
487     .parent     = TYPE_SYS_BUS_DEVICE,
488     .instance_init = aspeed_pcie_cfg_instance_init,
489     .instance_size = sizeof(AspeedPCIECfgState),
490     .class_init = aspeed_pcie_cfg_class_init,
491     .class_size = sizeof(AspeedPCIECfgClass),
492 };
493 
494 /*
495  * PCIe PHY
496  *
497  * PCIe Host Controller (PCIEH)
498  */
499 
500 /* AST2600 */
501 REG32(PEHR_ID,     0x00)
502     FIELD(PEHR_ID, DEV, 16, 16)
503 REG32(PEHR_CLASS_CODE,  0x04)
504 REG32(PEHR_DATALINK,    0x10)
505 REG32(PEHR_PROTECT,     0x7C)
506     FIELD(PEHR_PROTECT, LOCK, 0, 8)
507 REG32(PEHR_LINK,        0xC0)
508     FIELD(PEHR_LINK, STS, 5, 1)
509 
510 #define ASPEED_PCIE_PHY_UNLOCK  0xA8
511 
512 static uint64_t aspeed_pcie_phy_read(void *opaque, hwaddr addr,
513                                      unsigned int size)
514 {
515     AspeedPCIEPhyState *s = ASPEED_PCIE_PHY(opaque);
516     uint32_t reg = addr >> 2;
517     uint32_t value = 0;
518 
519     value = s->regs[reg];
520 
521     trace_aspeed_pcie_phy_read(s->id, addr, value);
522 
523     return value;
524 }
525 
526 static void aspeed_pcie_phy_write(void *opaque, hwaddr addr, uint64_t data,
527                                   unsigned int size)
528 {
529     AspeedPCIEPhyState *s = ASPEED_PCIE_PHY(opaque);
530     uint32_t reg = addr >> 2;
531 
532     trace_aspeed_pcie_phy_write(s->id, addr, data);
533 
534     switch (reg) {
535     case R_PEHR_PROTECT:
536         data &= R_PEHR_PROTECT_LOCK_MASK;
537         s->regs[reg] = !!(data == ASPEED_PCIE_PHY_UNLOCK);
538         break;
539     default:
540         s->regs[reg] = data;
541         break;
542     }
543 }
544 
545 static const MemoryRegionOps aspeed_pcie_phy_ops = {
546     .read = aspeed_pcie_phy_read,
547     .write = aspeed_pcie_phy_write,
548     .endianness = DEVICE_LITTLE_ENDIAN,
549     .valid = {
550         .min_access_size = 1,
551         .max_access_size = 4,
552     },
553 };
554 
555 static void aspeed_pcie_phy_reset(DeviceState *dev)
556 {
557     AspeedPCIEPhyState *s = ASPEED_PCIE_PHY(dev);
558     AspeedPCIEPhyClass *apc = ASPEED_PCIE_PHY_GET_CLASS(s);
559 
560     memset(s->regs, 0, apc->nr_regs << 2);
561 
562     s->regs[R_PEHR_ID] =
563         (0x1150 << R_PEHR_ID_DEV_SHIFT) | PCI_VENDOR_ID_ASPEED;
564     s->regs[R_PEHR_CLASS_CODE] = 0x06040006;
565     s->regs[R_PEHR_DATALINK] = 0xD7040022;
566     s->regs[R_PEHR_LINK] = R_PEHR_LINK_STS_MASK;
567 }
568 
569 static void aspeed_pcie_phy_realize(DeviceState *dev, Error **errp)
570 {
571     AspeedPCIEPhyState *s = ASPEED_PCIE_PHY(dev);
572     AspeedPCIEPhyClass *apc = ASPEED_PCIE_PHY_GET_CLASS(s);
573     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
574     g_autofree char *name;
575 
576     s->regs = g_new(uint32_t, apc->nr_regs);
577     name = g_strdup_printf(TYPE_ASPEED_PCIE_PHY ".regs.%d", s->id);
578     memory_region_init_io(&s->mmio, OBJECT(s), &aspeed_pcie_phy_ops, s, name,
579                           apc->nr_regs << 2);
580     sysbus_init_mmio(sbd, &s->mmio);
581 }
582 
583 static void aspeed_pcie_phy_unrealize(DeviceState *dev)
584 {
585     AspeedPCIEPhyState *s = ASPEED_PCIE_PHY(dev);
586 
587     g_free(s->regs);
588     s->regs = NULL;
589 }
590 
591 static const Property aspeed_pcie_phy_props[] = {
592     DEFINE_PROP_UINT32("id", AspeedPCIEPhyState, id, 0),
593 };
594 
595 static void aspeed_pcie_phy_class_init(ObjectClass *klass, const void *data)
596 {
597     DeviceClass *dc = DEVICE_CLASS(klass);
598     AspeedPCIEPhyClass *apc = ASPEED_PCIE_PHY_CLASS(klass);
599 
600     dc->desc = "ASPEED PCIe Phy";
601     dc->realize = aspeed_pcie_phy_realize;
602     dc->unrealize = aspeed_pcie_phy_unrealize;
603     device_class_set_legacy_reset(dc, aspeed_pcie_phy_reset);
604     device_class_set_props(dc, aspeed_pcie_phy_props);
605 
606     apc->nr_regs = 0x100 >> 2;
607 }
608 
609 static const TypeInfo aspeed_pcie_phy_info = {
610     .name       = TYPE_ASPEED_PCIE_PHY,
611     .parent     = TYPE_SYS_BUS_DEVICE,
612     .instance_size = sizeof(AspeedPCIEPhyState),
613     .class_init = aspeed_pcie_phy_class_init,
614     .class_size = sizeof(AspeedPCIEPhyClass),
615 };
616 
617 static void aspeed_pcie_register_types(void)
618 {
619     type_register_static(&aspeed_pcie_root_info);
620     type_register_static(&aspeed_pcie_rc_info);
621     type_register_static(&aspeed_pcie_cfg_info);
622     type_register_static(&aspeed_pcie_phy_info);
623 }
624 
625 type_init(aspeed_pcie_register_types);
626 
627