+namespace {
+
+class Msi_res : public Hw::Msi_resource
+{
+public:
+ Msi_res(unsigned msi, Pci_bridge *bus, Hw::Pci::Cfg_addr cfg_addr)
+ : Msi_resource(msi), _bus(bus), _cfg_addr(cfg_addr)
+ {}
+
+ int bind(L4::Cap<L4::Irq> irq, unsigned mode);
+ int unbind();
+
+ void dump(int indent) const
+ { Adr_resource::dump("MSI ", indent); }
+
+private:
+ Pci_bridge *_bus;
+ Hw::Pci::Cfg_addr _cfg_addr;
+};
+
+int
+Msi_res::bind(L4::Cap<L4::Irq> irq, unsigned mode)
+{
+ using namespace Hw::Pci;
+
+ int err = Msi_resource::bind(irq, mode);
+ if (err < 0)
+ return err;
+
+ l4_umword_t msg = 0;
+ int e2 = l4_error(system_icu()->icu->msi_info(pin(), &msg));
+ if (e2 < 0)
+ {
+ d_printf(DBG_ERR, "ERROR: could not get MSI message (pin=%x)\n", pin());
+ return e2;
+ }
+
+ // MSI capability
+ l4_uint32_t ctl;
+ int msg_offs = 8;
+
+ _bus->cfg_read(_cfg_addr + 2, &ctl, Cfg_short);
+ if (ctl & (1 << 7))
+ msg_offs = 12;
+
+ _bus->cfg_write(_cfg_addr + 4, Msi::Base_lo | Msi::Dest_mode_phys | Msi::Dest_redir_cpu, Cfg_long);
+
+ if (ctl & (1 << 7))
+ _bus->cfg_write(_cfg_addr + 8, Msi::Base_hi, Cfg_long);
+
+ _bus->cfg_write(_cfg_addr + msg_offs, Msi::Data_level_assert | Msi::Data_trigger_edge | Msi::Data_del_fixed | Msi::data(msg), Cfg_short);
+
+ _bus->cfg_write(_cfg_addr + 2, ctl | 1, Cfg_short);
+
+ d_printf(DBG_DEBUG2, "MSI: enable kernel PIN=%x hwpci=%02x:%02x.%x: reg=%03x msg=%lx\n",
+ pin(), _cfg_addr.bus(), _cfg_addr.dev(), _cfg_addr.fn(),
+ _cfg_addr.reg(), msg);
+
+ return err;
+}
+
+int
+Msi_res::unbind()
+{
+ using namespace Hw::Pci;
+ // disable MSI
+ l4_uint32_t ctl;
+ _bus->cfg_read(_cfg_addr + 2, &ctl, Cfg_short);
+ _bus->cfg_write(_cfg_addr + 2, ctl & ~1, Cfg_short);
+
+ return Msi_resource::unbind();
+}
+
+}
+