Add PCI interrupt method.

A PCI device passes an interrupt to its host. The host will determine from the PCI device which interrupt to trigger.
This commit is contained in:
joevt 2023-09-18 17:24:50 -07:00 committed by dingusdev
parent be80595834
commit 6503a300cc
5 changed files with 60 additions and 2 deletions

View File

@ -70,6 +70,9 @@ enum {
*/
class BanditHost : public PCIHost, public MMIODevice {
public:
// PCIHost methods
virtual void pci_interrupt(uint8_t irq_line_state, PCIBase *dev) {}
// MMIODevice methods
uint32_t read(uint32_t rgn_start, uint32_t offset, int size);
void write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size);

View File

@ -115,15 +115,20 @@ public:
virtual void set_host(PCIHost* host_instance) {
this->host_instance = host_instance;
};
}
virtual void set_multi_function(bool is_multi_function) {
this->hdr_type = is_multi_function ? (this->hdr_type | 0x80) : (this->hdr_type & 0x7f);
}
virtual void set_irq_pin(uint8_t irq_pin) {
this->irq_pin = irq_pin;
}
virtual void pci_interrupt(uint8_t irq_line_state) {
this->host_instance->pci_interrupt(irq_line_state, this);
}
// MMIODevice methods
virtual uint32_t read(uint32_t rgn_start, uint32_t offset, int size) { return 0; }
virtual void write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size) { }

View File

@ -77,6 +77,7 @@ public:
virtual PCIBase *pci_find_device(uint8_t bus_num, uint8_t dev_num, uint8_t fun_num);
virtual void pci_interrupt(uint8_t irq_line_state, PCIBase *dev) {}
protected:
std::unordered_map<int, PCIBase*> dev_map;

View File

@ -22,6 +22,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
/** MPC106 (Grackle) emulation. */
#include <devices/common/hwcomponent.h>
#include <devices/common/hwinterrupt.h>
#include <devices/deviceregistry.h>
#include <devices/memctrl/memctrlbase.h>
#include <devices/memctrl/mpc106.h>
@ -68,9 +69,45 @@ int MPC106::device_postinit()
this->attach_pci_device(pci_dev_name, slot.second);
}
}
this->int_ctrl = dynamic_cast<InterruptCtrl*>(
gMachineObj->get_comp_by_type(HWCompType::INT_CTRL));
this->irq_id_PCI_A = this->int_ctrl->register_dev_int(IntSrc::PCI_A );
this->irq_id_PCI_B = this->int_ctrl->register_dev_int(IntSrc::PCI_B );
this->irq_id_PCI_C = this->int_ctrl->register_dev_int(IntSrc::PCI_C );
this->irq_id_PCI_GPU = this->int_ctrl->register_dev_int(IntSrc::PCI_GPU );
this->irq_id_PCI_PERCH = this->int_ctrl->register_dev_int(IntSrc::PCI_PERCH);
return 0;
}
void MPC106::pci_interrupt(uint8_t irq_line_state, PCIBase *dev) {
auto it = std::find_if(dev_map.begin(), dev_map.end(),
[&dev](const std::pair<int, PCIBase*> &p) {
return p.second == dev;
}
);
if (it == dev_map.end()) {
LOG_F(ERROR, "Interrupt from unknown device %s", dev->get_name().c_str());
}
else {
uint32_t irq_id;
switch (it->first) {
case DEV_FUN(0x0C,0): irq_id = this->irq_id_PCI_PERCH; break;
case DEV_FUN(0x0D,0): irq_id = this->irq_id_PCI_A ; break;
case DEV_FUN(0x0E,0): irq_id = this->irq_id_PCI_B ; break;
case DEV_FUN(0x0F,0): irq_id = this->irq_id_PCI_C ; break;
case DEV_FUN(0x12,0): irq_id = this->irq_id_PCI_GPU ; break;
default:
LOG_F(ERROR, "Interrupt from device %s at unexpected device/function %02x.%x", dev->get_name().c_str(), it->first >> 3, it->first & 7);
return;
}
if (this->int_ctrl)
this->int_ctrl->ack_int(irq_id, irq_line_state);
}
}
uint32_t MPC106::read(uint32_t rgn_start, uint32_t offset, int size) {
if (rgn_start == 0xFE000000) {
return pci_io_read_broadcast(offset, size);

View File

@ -36,11 +36,13 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <devices/common/pci/pcidevice.h>
#include <devices/common/pci/pcihost.h>
#include <devices/memctrl/memctrlbase.h>
#include <machines/machinebase.h>
#include <cinttypes>
#include <memory>
class InterruptCtrl;
/** Grackle configuration space registers. */
enum GrackleReg : uint32_t {
CFG10 = 0x40, // bus # + subordinate bus # + disconnect counter
@ -79,6 +81,8 @@ public:
uint32_t read(uint32_t rgn_start, uint32_t offset, int size);
void write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size);
virtual void pci_interrupt(uint8_t irq_line_state, PCIBase *dev);
int device_postinit();
protected:
@ -110,6 +114,14 @@ private:
uint32_t mem_end[2] = {};
uint32_t ext_mem_end[2] = {};
uint8_t mem_bank_en = 0;
// interrupt related stuff
InterruptCtrl* int_ctrl = nullptr;
uint32_t irq_id_PCI_A = 0;
uint32_t irq_id_PCI_B = 0;
uint32_t irq_id_PCI_C = 0;
uint32_t irq_id_PCI_GPU = 0;
uint32_t irq_id_PCI_PERCH = 0;
};
#endif // MPC106_H