]>
Commit | Line | Data |
---|---|---|
9d5154d7 MA |
1 | /* |
2 | * Base class for PCI Express Root Ports | |
3 | * | |
4 | * Copyright (C) 2017 Red Hat Inc | |
5 | * | |
6 | * Authors: | |
7 | * Marcel Apfelbaum <[email protected]> | |
8 | * | |
9 | * Most of the code was migrated from hw/pci-bridge/ioh3420. | |
10 | * | |
11 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
12 | * See the COPYING file in the top-level directory. | |
13 | */ | |
14 | ||
15 | #include "qemu/osdep.h" | |
16 | #include "qapi/error.h" | |
17 | #include "hw/pci/pcie_port.h" | |
18 | ||
19 | static void rp_aer_vector_update(PCIDevice *d) | |
20 | { | |
21 | PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(d); | |
22 | ||
23 | if (rpc->aer_vector) { | |
24 | pcie_aer_root_set_vector(d, rpc->aer_vector(d)); | |
25 | } | |
26 | } | |
27 | ||
28 | static void rp_write_config(PCIDevice *d, uint32_t address, | |
29 | uint32_t val, int len) | |
30 | { | |
31 | uint32_t root_cmd = | |
32 | pci_get_long(d->config + d->exp.aer_cap + PCI_ERR_ROOT_COMMAND); | |
33 | ||
34 | pci_bridge_write_config(d, address, val, len); | |
35 | rp_aer_vector_update(d); | |
36 | pcie_cap_slot_write_config(d, address, val, len); | |
37 | pcie_aer_write_config(d, address, val, len); | |
38 | pcie_aer_root_write_config(d, address, val, len, root_cmd); | |
39 | } | |
40 | ||
41 | static void rp_reset(DeviceState *qdev) | |
42 | { | |
43 | PCIDevice *d = PCI_DEVICE(qdev); | |
44 | ||
45 | rp_aer_vector_update(d); | |
46 | pcie_cap_root_reset(d); | |
47 | pcie_cap_deverr_reset(d); | |
48 | pcie_cap_slot_reset(d); | |
49 | pcie_cap_arifwd_reset(d); | |
50 | pcie_aer_root_reset(d); | |
51 | pci_bridge_reset(qdev); | |
52 | pci_bridge_disable_base_limit(d); | |
53 | } | |
54 | ||
55 | static void rp_realize(PCIDevice *d, Error **errp) | |
56 | { | |
57 | PCIEPort *p = PCIE_PORT(d); | |
58 | PCIESlot *s = PCIE_SLOT(d); | |
59 | PCIDeviceClass *dc = PCI_DEVICE_GET_CLASS(d); | |
60 | PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(d); | |
61 | int rc; | |
9d5154d7 MA |
62 | |
63 | pci_config_set_interrupt_pin(d->config, 1); | |
64 | pci_bridge_initfn(d, TYPE_PCIE_BUS); | |
65 | pcie_port_init_reg(d); | |
66 | ||
f8cd1b02 MZ |
67 | rc = pci_bridge_ssvid_init(d, rpc->ssvid_offset, dc->vendor_id, |
68 | rpc->ssid, errp); | |
9d5154d7 | 69 | if (rc < 0) { |
f8cd1b02 | 70 | error_append_hint(errp, "Can't init SSV ID, error %d\n", rc); |
9d5154d7 MA |
71 | goto err_bridge; |
72 | } | |
73 | ||
74 | if (rpc->interrupts_init) { | |
f8cd1b02 | 75 | rc = rpc->interrupts_init(d, errp); |
9d5154d7 | 76 | if (rc < 0) { |
9d5154d7 MA |
77 | goto err_bridge; |
78 | } | |
79 | } | |
80 | ||
f8cd1b02 MZ |
81 | rc = pcie_cap_init(d, rpc->exp_offset, PCI_EXP_TYPE_ROOT_PORT, |
82 | p->port, errp); | |
9d5154d7 | 83 | if (rc < 0) { |
f8cd1b02 MZ |
84 | error_append_hint(errp, "Can't add Root Port capability, " |
85 | "error %d\n", rc); | |
9d5154d7 MA |
86 | goto err_int; |
87 | } | |
88 | ||
89 | pcie_cap_arifwd_init(d); | |
90 | pcie_cap_deverr_init(d); | |
91 | pcie_cap_slot_init(d, s->slot); | |
92 | pcie_cap_root_init(d); | |
93 | ||
94 | pcie_chassis_create(s->chassis); | |
95 | rc = pcie_chassis_add_slot(s); | |
96 | if (rc < 0) { | |
97 | error_setg(errp, "Can't add chassis slot, error %d", rc); | |
98 | goto err_pcie_cap; | |
99 | } | |
100 | ||
101 | rc = pcie_aer_init(d, PCI_ERR_VER, rpc->aer_offset, | |
f8cd1b02 | 102 | PCI_ERR_SIZEOF, errp); |
9d5154d7 | 103 | if (rc < 0) { |
9d5154d7 MA |
104 | goto err; |
105 | } | |
106 | pcie_aer_root_init(d); | |
107 | rp_aer_vector_update(d); | |
108 | ||
109 | return; | |
110 | ||
111 | err: | |
112 | pcie_chassis_del_slot(s); | |
113 | err_pcie_cap: | |
114 | pcie_cap_exit(d); | |
115 | err_int: | |
116 | if (rpc->interrupts_uninit) { | |
117 | rpc->interrupts_uninit(d); | |
118 | } | |
119 | err_bridge: | |
120 | pci_bridge_exitfn(d); | |
121 | } | |
122 | ||
123 | static void rp_exit(PCIDevice *d) | |
124 | { | |
125 | PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(d); | |
126 | PCIESlot *s = PCIE_SLOT(d); | |
127 | ||
128 | pcie_aer_exit(d); | |
129 | pcie_chassis_del_slot(s); | |
130 | pcie_cap_exit(d); | |
131 | if (rpc->interrupts_uninit) { | |
132 | rpc->interrupts_uninit(d); | |
133 | } | |
134 | pci_bridge_exitfn(d); | |
135 | } | |
136 | ||
137 | static Property rp_props[] = { | |
138 | DEFINE_PROP_BIT(COMPAT_PROP_PCP, PCIDevice, cap_present, | |
139 | QEMU_PCIE_SLTCAP_PCP_BITNR, true), | |
140 | DEFINE_PROP_END_OF_LIST() | |
141 | }; | |
142 | ||
143 | static void rp_class_init(ObjectClass *klass, void *data) | |
144 | { | |
145 | DeviceClass *dc = DEVICE_CLASS(klass); | |
146 | PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); | |
147 | ||
9d5154d7 MA |
148 | k->is_bridge = 1; |
149 | k->config_write = rp_write_config; | |
150 | k->realize = rp_realize; | |
151 | k->exit = rp_exit; | |
152 | set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); | |
153 | dc->reset = rp_reset; | |
154 | dc->props = rp_props; | |
155 | } | |
156 | ||
157 | static const TypeInfo rp_info = { | |
158 | .name = TYPE_PCIE_ROOT_PORT, | |
159 | .parent = TYPE_PCIE_SLOT, | |
160 | .class_init = rp_class_init, | |
161 | .abstract = true, | |
162 | .class_size = sizeof(PCIERootPortClass), | |
71d78767 EH |
163 | .interfaces = (InterfaceInfo[]) { |
164 | { INTERFACE_PCIE_DEVICE }, | |
165 | { } | |
166 | }, | |
9d5154d7 MA |
167 | }; |
168 | ||
169 | static void rp_register_types(void) | |
170 | { | |
171 | type_register_static(&rp_info); | |
172 | } | |
173 | ||
174 | type_init(rp_register_types) |