Revision 6bde6aaa hw/pcie.c
b/hw/pcie.c | ||
---|---|---|
140 | 140 |
PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE); |
141 | 141 |
} |
142 | 142 |
|
143 |
static void hotplug_event_update_event_status(PCIDevice *dev) |
|
144 |
{ |
|
145 |
uint32_t pos = dev->exp.exp_cap; |
|
146 |
uint8_t *exp_cap = dev->config + pos; |
|
147 |
uint16_t sltctl = pci_get_word(exp_cap + PCI_EXP_SLTCTL); |
|
148 |
uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA); |
|
149 |
|
|
150 |
dev->exp.hpev_notified = (sltctl & PCI_EXP_SLTCTL_HPIE) && |
|
151 |
(sltsta & sltctl & PCI_EXP_HP_EV_SUPPORTED); |
|
152 |
} |
|
153 |
|
|
154 |
static void hotplug_event_notify(PCIDevice *dev) |
|
155 |
{ |
|
156 |
bool prev = dev->exp.hpev_notified; |
|
157 |
|
|
158 |
hotplug_event_update_event_status(dev); |
|
159 |
|
|
160 |
if (prev == dev->exp.hpev_notified) { |
|
161 |
return; |
|
162 |
} |
|
163 |
|
|
164 |
/* Note: the logic above does not take into account whether interrupts |
|
165 |
* are masked. The result is that interrupt will be sent when it is |
|
166 |
* subsequently unmasked. This appears to be legal: Section 6.7.3.4: |
|
167 |
* The Port may optionally send an MSI when there are hot-plug events that |
|
168 |
* occur while interrupt generation is disabled, and interrupt generation is |
|
169 |
* subsequently enabled. */ |
|
170 |
if (!pci_msi_enabled(dev)) { |
|
171 |
qemu_set_irq(dev->irq[dev->exp.hpev_intx], dev->exp.hpev_notified); |
|
172 |
} else if (dev->exp.hpev_notified) { |
|
173 |
pci_msi_notify(dev, pcie_cap_flags_get_vector(dev)); |
|
174 |
} |
|
175 |
} |
|
176 |
|
|
143 | 177 |
/* |
144 | 178 |
* A PCI Express Hot-Plug Event has occured, so update slot status register |
145 | 179 |
* and notify OS of the event if necessary. |
... | ... | |
149 | 183 |
*/ |
150 | 184 |
static void pcie_cap_slot_event(PCIDevice *dev, PCIExpressHotPlugEvent event) |
151 | 185 |
{ |
152 |
uint8_t *exp_cap = dev->config + dev->exp.exp_cap; |
|
153 |
uint16_t sltctl = pci_get_word(exp_cap + PCI_EXP_SLTCTL); |
|
154 |
uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA); |
|
155 |
|
|
156 |
PCIE_DEV_PRINTF(dev, |
|
157 |
"sltctl: 0x%02"PRIx16" sltsta: 0x%02"PRIx16" event: %x\n", |
|
158 |
sltctl, sltsta, event); |
|
159 |
|
|
160 |
if (pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA, event)) { |
|
186 |
/* Minor optimization: if nothing changed - no event is needed. */ |
|
187 |
if (pci_word_test_and_set_mask(dev->config + dev->exp.exp_cap + |
|
188 |
PCI_EXP_SLTSTA, event)) { |
|
161 | 189 |
return; |
162 | 190 |
} |
163 |
sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA); |
|
164 |
PCIE_DEV_PRINTF(dev, "sltsta -> %02"PRIx16"\n", sltsta); |
|
165 |
|
|
166 |
if ((sltctl & PCI_EXP_SLTCTL_HPIE) && |
|
167 |
(sltctl & event & PCI_EXP_HP_EV_SUPPORTED)) { |
|
168 |
if (pci_msi_enabled(dev)) { |
|
169 |
pci_msi_notify(dev, pcie_cap_flags_get_vector(dev)); |
|
170 |
} else { |
|
171 |
qemu_set_irq(dev->irq[dev->exp.hpev_intx], 1); |
|
172 |
} |
|
173 |
} |
|
191 |
hotplug_event_notify(dev); |
|
174 | 192 |
} |
175 | 193 |
|
176 | 194 |
static int pcie_cap_slot_hotplug(DeviceState *qdev, |
... | ... | |
258 | 276 |
pci_word_test_and_set_mask(dev->w1cmask + pos + PCI_EXP_SLTSTA, |
259 | 277 |
PCI_EXP_HP_EV_SUPPORTED); |
260 | 278 |
|
279 |
dev->exp.hpev_notified = false; |
|
280 |
|
|
261 | 281 |
pci_bus_hotplug(pci_bridge_get_sec_bus(DO_UPCAST(PCIBridge, dev, dev)), |
262 | 282 |
pcie_cap_slot_hotplug, &dev->qdev); |
263 | 283 |
} |
... | ... | |
286 | 306 |
PCI_EXP_SLTSTA_CC | |
287 | 307 |
PCI_EXP_SLTSTA_PDC | |
288 | 308 |
PCI_EXP_SLTSTA_ABP); |
309 |
|
|
310 |
hotplug_event_notify(dev); |
|
289 | 311 |
} |
290 | 312 |
|
291 | 313 |
void pcie_cap_slot_write_config(PCIDevice *dev, |
292 |
uint32_t addr, uint32_t val, int len, |
|
293 |
uint16_t sltctl_prev) |
|
314 |
uint32_t addr, uint32_t val, int len) |
|
294 | 315 |
{ |
295 | 316 |
uint32_t pos = dev->exp.exp_cap; |
296 | 317 |
uint8_t *exp_cap = dev->config + pos; |
297 |
uint16_t sltctl = pci_get_word(exp_cap + PCI_EXP_SLTCTL); |
|
298 | 318 |
uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA); |
299 | 319 |
|
300 | 320 |
if (!ranges_overlap(addr, len, pos + PCI_EXP_SLTCTL, 2)) { |
301 | 321 |
return; |
302 | 322 |
} |
303 | 323 |
|
304 |
PCIE_DEV_PRINTF(dev, |
|
305 |
"addr: 0x%"PRIx32" val: 0x%"PRIx32" len: %d\n" |
|
306 |
"\tsltctl_prev: 0x%02"PRIx16" sltctl: 0x%02"PRIx16 |
|
307 |
" sltsta: 0x%02"PRIx16"\n", |
|
308 |
addr, val, len, sltctl_prev, sltctl, sltsta); |
|
309 |
|
|
310 |
/* SLTCTL */ |
|
311 |
PCIE_DEV_PRINTF(dev, "sltctl: 0x%02"PRIx16" -> 0x%02"PRIx16"\n", |
|
312 |
sltctl_prev, sltctl); |
|
313 |
|
|
314 | 324 |
if (pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTCTL, |
315 | 325 |
PCI_EXP_SLTCTL_EIC)) { |
316 | 326 |
sltsta ^= PCI_EXP_SLTSTA_EIS; /* toggle PCI_EXP_SLTSTA_EIS bit */ |
... | ... | |
320 | 330 |
sltsta); |
321 | 331 |
} |
322 | 332 |
|
323 |
/* |
|
324 |
* The events control bits might be enabled or disabled, |
|
325 |
* Check if the software notificastion condition is satisfied |
|
326 |
* or disatisfied. |
|
327 |
* |
|
328 |
* 6.7.3.4 Software Notification of Hot-plug events |
|
329 |
*/ |
|
330 |
if (pci_msi_enabled(dev)) { |
|
331 |
bool msi_trigger = |
|
332 |
(sltctl & PCI_EXP_SLTCTL_HPIE) && |
|
333 |
((sltctl_prev ^ sltctl) & sltctl & /* stlctl: 0 -> 1 */ |
|
334 |
sltsta & PCI_EXP_HP_EV_SUPPORTED); |
|
335 |
if (msi_trigger) { |
|
336 |
pci_msi_notify(dev, pcie_cap_flags_get_vector(dev)); |
|
337 |
} |
|
338 |
} else { |
|
339 |
int int_level = |
|
340 |
(sltctl & PCI_EXP_SLTCTL_HPIE) && |
|
341 |
(sltctl & sltsta & PCI_EXP_HP_EV_SUPPORTED); |
|
342 |
qemu_set_irq(dev->irq[dev->exp.hpev_intx], int_level); |
|
343 |
} |
|
344 |
|
|
345 |
if (!((sltctl_prev ^ sltctl) & PCI_EXP_SLTCTL_SUPPORTED)) { |
|
346 |
PCIE_DEV_PRINTF(dev, |
|
347 |
"sprious command completion slctl " |
|
348 |
"0x%"PRIx16" -> 0x%"PRIx16"\n", |
|
349 |
sltctl_prev, sltctl); |
|
350 |
} |
|
333 |
hotplug_event_notify(dev); |
|
351 | 334 |
|
352 | 335 |
/* |
353 | 336 |
* 6.7.3.2 Command Completed Events |
... | ... | |
368 | 351 |
pcie_cap_slot_event(dev, PCI_EXP_HP_EV_CCI); |
369 | 352 |
} |
370 | 353 |
|
354 |
int pcie_cap_slot_post_load(void *opaque, int version_id) |
|
355 |
{ |
|
356 |
PCIDevice *dev = opaque; |
|
357 |
hotplug_event_update_event_status(dev); |
|
358 |
return 0; |
|
359 |
} |
|
360 |
|
|
371 | 361 |
void pcie_cap_slot_push_attention_button(PCIDevice *dev) |
372 | 362 |
{ |
373 | 363 |
pcie_cap_slot_event(dev, PCI_EXP_HP_EV_ABP); |
Also available in: Unified diff