2 * (c) 2013 Alexander Warg <warg@os.inf.tu-dresden.de>
3 * economic rights: Technische Universität Dresden (Germany)
5 * This file is part of TUD:OS and distributed under the terms of the
6 * GNU General Public License 2.
7 * Please see the COPYING-GPL-2 file for details.
17 namespace Hw { namespace Pci {
21 class Saved_msi_cap : public Saved_cap
24 explicit Saved_msi_cap(unsigned pos) : Saved_cap(Cap::Msi, pos) {}
36 l4_uint32_t addr_lo, addr_hi;
40 void _save(Config cap)
42 cap.read(Control, &control);
43 cap.read(Addr_lo, &addr_lo);
44 if (control & (1 << 7))
46 cap.read(Addr_hi, &addr_hi);
47 cap.read(Data_64, &data);
50 cap.read(Data_32, &data);
53 void _restore(Config cap)
55 cap.write(Addr_lo, addr_lo);
56 if (control & (1 << 7))
58 cap.write(Addr_hi, addr_hi);
59 cap.write(Data_64, data);
62 cap.write(Data_32, data);
63 cap.write(Control, control);
68 static unsigned _last_msi;
81 Dest_mode_phys = 0 << 2,
82 Dest_mode_log = 1 << 2,
87 Dest_redir_cpu = 0 << 3,
88 Dest_redir_lowprio = 1 << 3,
93 Data_del_fixed = 0 << 8,
94 Data_del_lowprio = 1 << 8,
99 Data_level_deassert = 0 << 14,
100 Data_level_assert = 1 << 14,
105 Data_trigger_edge = 0 << 15,
106 Data_trigger_level = 1 << 15,
109 static unsigned data(int vector) { return vector & 0xff; }
112 class Msi_res : public Hw::Msi_resource, public Transparent_msi
115 Msi_res(unsigned msi, Dev *dev, l4_uint32_t cap_offset)
116 : Msi_resource(msi), _dev(dev), _cap(cap_offset)
119 int bind(Triggerable const &irq, unsigned mode) override;
120 int unbind(bool deleted) override;
121 int msi_info(Msi_src *, l4_icu_msi_info_t *) override
122 { return -L4_EINVAL; }
124 void dump(int indent) const override
125 { Resource::dump("MSI ", indent); }
127 l4_uint32_t filter_cmd_read(l4_uint32_t cmd) override;
128 l4_uint16_t filter_cmd_write(l4_uint16_t cmd, l4_uint16_t ocmd) override;
131 void setup_cap(l4_uint16_t ctl, l4_uint16_t cmd);
134 l4_icu_msi_info_t _msg;
138 Msi_res::setup_cap(l4_uint16_t ctl, l4_uint16_t cmd)
140 unsigned msg_offs = 8;
144 // disable INTx if not already
145 if (!(cmd & Dev::CC_int_disable))
146 _dev->cfg_write<l4_uint16_t>(Config::Command, cmd | Dev::CC_int_disable);
148 _dev->cfg_write<l4_uint32_t>(_cap + 4, _msg.msi_addr);
151 _dev->cfg_write<l4_uint32_t>(_cap + 8, _msg.msi_addr >> 32);
153 _dev->cfg_write<l4_uint16_t>(_cap + msg_offs, _msg.msi_data);
155 _dev->cfg_write<l4_uint16_t>(_cap + 2, ctl | 1);
157 d_printf(DBG_DEBUG2, "MSI: enable kernel PIN=%x hwpci=%02x:%02x.%x: reg=%03x msg=%llx:%x\n",
158 pin(), _dev->bus_nr(), _dev->device_nr(), _dev->function_nr(),
159 _cap, _msg.msi_addr, _msg.msi_data);
163 Msi_res::bind(Triggerable const &irq, unsigned mode)
165 int err = Msi_resource::bind(irq, mode);
169 _msg = l4_icu_msi_info_t();
170 // TODO: use a source filter for transparent MSIs too
171 int e2 = l4_error(system_icu()->icu->msi_info(pin(), 0, &_msg));
174 d_printf(DBG_ERR, "ERROR: could not get MSI message (pin=%x)\n", pin());
175 Msi_resource::unbind(false);
180 l4_uint16_t ctl = _dev->cfg_read<l4_uint16_t>(_cap + 2);
181 setup_cap(ctl, _dev->cfg_read<l4_uint16_t>(Config::Command));
186 Msi_res::unbind(bool deleted)
190 ctl = _dev->cfg_read<l4_uint16_t>(_cap + 2);
191 _dev->cfg_write<l4_uint16_t>(_cap + 2, ctl & ~1);
193 return Msi_resource::unbind(deleted);
197 Msi_res::filter_cmd_read(l4_uint32_t cmd)
201 // disable INTx if not already
202 if (!(cmd & Dev::CC_int_disable))
203 _dev->cfg_write<l4_uint16_t>(Config::Command, cmd | Dev::CC_int_disable);
205 // no transparent MSI bound, nothing to check
209 l4_uint16_t ctl = _dev->cfg_read<l4_uint16_t>(_cap + 2);
210 if (!(cmd & Dev::CC_int_disable) || !(ctl & 1))
211 // MSI was disabled, rewrite the MSI cap
218 Msi_res::filter_cmd_write(l4_uint16_t cmd, l4_uint16_t ocmd)
222 cmd |= Dev::CC_int_disable;
226 l4_uint16_t ctl = _dev->cfg_read<l4_uint16_t>(_cap + 2);
227 if (!(ocmd & Dev::CC_int_disable) || !(ctl & 1))
228 // MSI was disabled, rewrite the MSI cap
229 setup_cap(ctl, ocmd);
231 cmd |= Dev::CC_int_disable;
240 Dev::parse_msi_cap(Cfg_addr cap_ptr)
242 if ( !Io_config::cfg->transparent_msi(host())
243 || !system_icu()->info.supports_msi())
246 unsigned msi = _last_msi++;
248 for (Resource_list::const_iterator i = host()->resources()->begin();
249 i != host()->resources()->end(); ++i)
251 if ((*i)->type() == Resource::Irq_res)
254 (*i)->add_flags(Resource::F_disabled);
258 d_printf(DBG_DEBUG, "Use MSI PCI device %02x:%02x:%x: pin=%x\n",
259 bus()->num, host()->adr() >> 16, host()->adr() & 0xff, msi);
261 auto *res = new Msi_res(msi, this, cap_ptr.reg());
263 _host->add_resource_rq(res);
265 _saved_state.add_cap(new Saved_msi_cap(cap_ptr.reg()));