]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/io/io/server/src/pci/msi.cc
Update
[l4.git] / l4 / pkg / io / io / server / src / pci / msi.cc
1 /*
2  * (c) 2013 Alexander Warg <warg@os.inf.tu-dresden.de>
3  *     economic rights: Technische Universität Dresden (Germany)
4  *
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.
8  */
9
10 #include "pci.h"
11
12 #include "cfg.h"
13 #include "debug.h"
14 #include "hw_msi.h"
15 #include "main.h"
16
17 namespace Hw { namespace Pci {
18
19 namespace {
20
21 class Saved_msi_cap : public Saved_cap
22 {
23 public:
24   explicit Saved_msi_cap(unsigned pos) : Saved_cap(Cap::Msi, pos) {}
25
26 private:
27   enum
28   {
29     Control = 0x02,
30     Addr_lo = 0x04,
31     Addr_hi = 0x08,
32     Data_32 = 0x08,
33     Data_64 = 0x0c
34   };
35
36   l4_uint32_t addr_lo, addr_hi;
37   l4_uint16_t data;
38   l4_uint16_t control;
39
40   void _save(Config cap)
41   {
42     cap.read(Control, &control);
43     cap.read(Addr_lo, &addr_lo);
44     if (control & (1 << 7))
45       {
46         cap.read(Addr_hi, &addr_hi);
47         cap.read(Data_64, &data);
48       }
49     else
50       cap.read(Data_32, &data);
51   }
52
53   void _restore(Config cap)
54   {
55     cap.write(Addr_lo, addr_lo);
56     if (control & (1 << 7))
57       {
58         cap.write(Addr_hi, addr_hi);
59         cap.write(Data_64, data);
60       }
61     else
62       cap.write(Data_32, data);
63     cap.write(Control, control);
64   }
65
66 };
67
68 static unsigned _last_msi;
69
70 class Msi
71 {
72 public:
73   enum Addrs
74   {
75     Base_hi = 0,
76     Base_lo = 0xfee00000,
77   };
78
79   enum Mode
80   {
81     Dest_mode_phys = 0 << 2,
82     Dest_mode_log  = 1 << 2,
83   };
84
85   enum Redir
86   {
87     Dest_redir_cpu     = 0 << 3,
88     Dest_redir_lowprio = 1 << 3,
89   };
90
91   enum Delivery
92   {
93     Data_del_fixed   = 0 << 8,
94     Data_del_lowprio = 1 << 8,
95   };
96
97   enum Level
98   {
99     Data_level_deassert = 0 << 14,
100     Data_level_assert   = 1 << 14,
101   };
102
103   enum Trigger
104   {
105     Data_trigger_edge  = 0 << 15,
106     Data_trigger_level = 1 << 15,
107   };
108
109   static unsigned data(int vector) { return vector & 0xff; }
110 };
111
112 class Msi_res : public Hw::Msi_resource, public Transparent_msi
113 {
114 public:
115   Msi_res(unsigned msi, Dev *dev, l4_uint32_t cap_offset)
116   : Msi_resource(msi), _dev(dev), _cap(cap_offset)
117   {}
118
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; }
123
124   void dump(int indent) const override
125   { Resource::dump("MSI   ", indent);  }
126
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;
129
130 private:
131   void setup_cap(l4_uint16_t ctl, l4_uint16_t cmd);
132   Dev *_dev;
133   l4_uint32_t _cap;
134   l4_icu_msi_info_t _msg;
135 };
136
137 void
138 Msi_res::setup_cap(l4_uint16_t ctl, l4_uint16_t cmd)
139 {
140   unsigned msg_offs = 8;
141   if (ctl & (1 << 7))
142     msg_offs = 12;
143
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);
147
148   _dev->cfg_write<l4_uint32_t>(_cap + 4, _msg.msi_addr);
149
150   if (ctl & (1 << 7))
151     _dev->cfg_write<l4_uint32_t>(_cap + 8, _msg.msi_addr >> 32);
152
153   _dev->cfg_write<l4_uint16_t>(_cap + msg_offs, _msg.msi_data);
154
155   _dev->cfg_write<l4_uint16_t>(_cap + 2, ctl | 1);
156
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);
160 }
161
162 int
163 Msi_res::bind(Triggerable const &irq, unsigned mode)
164 {
165   int err = Msi_resource::bind(irq, mode);
166   if (err < 0)
167     return err;
168
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));
172   if (e2 < 0)
173     {
174       d_printf(DBG_ERR, "ERROR: could not get MSI message (pin=%x)\n", pin());
175       Msi_resource::unbind(false);
176       return e2;
177     }
178
179   // MSI capability
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));
182   return 0;
183 }
184
185 int
186 Msi_res::unbind(bool deleted)
187 {
188   // disable MSI
189   l4_uint16_t ctl;
190   ctl = _dev->cfg_read<l4_uint16_t>(_cap + 2);
191   _dev->cfg_write<l4_uint16_t>(_cap + 2, ctl & ~1);
192
193   return Msi_resource::unbind(deleted);
194 }
195
196 l4_uint32_t
197 Msi_res::filter_cmd_read(l4_uint32_t cmd)
198 {
199   if (!this->irq())
200     {
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);
204
205       // no transparent MSI bound, nothing to check
206       return cmd;
207     }
208
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
212     setup_cap(ctl, cmd);
213
214   return cmd;
215 }
216
217 l4_uint16_t
218 Msi_res::filter_cmd_write(l4_uint16_t cmd, l4_uint16_t ocmd)
219 {
220   if (!this->irq())
221     {
222       cmd |= Dev::CC_int_disable;
223       return cmd;
224     }
225
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);
230
231   cmd |= Dev::CC_int_disable;
232   return cmd;
233
234 }
235
236 }
237
238
239 void
240 Dev::parse_msi_cap(Cfg_addr cap_ptr)
241 {
242   if (   !Io_config::cfg->transparent_msi(host())
243       || !system_icu()->info.supports_msi())
244     return;
245
246   unsigned msi = _last_msi++;
247
248   for (Resource_list::const_iterator i = host()->resources()->begin();
249       i != host()->resources()->end(); ++i)
250     {
251       if ((*i)->type() == Resource::Irq_res)
252         {
253           (*i)->set_empty();
254           (*i)->add_flags(Resource::F_disabled);
255         }
256     }
257
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);
260
261   auto *res = new Msi_res(msi, this, cap_ptr.reg());
262   flags.msi() = true;
263   _host->add_resource_rq(res);
264   _transp_msi = res;
265   _saved_state.add_cap(new Saved_msi_cap(cap_ptr.reg()));
266 }
267
268 }}