]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/io/server/src/pci.h
update
[l4.git] / l4 / pkg / io / server / src / pci.h
1 /*
2  * (c) 2010 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
3  *          Alexander Warg <warg@os.inf.tu-dresden.de>
4  *     economic rights: Technische Universität Dresden (Germany)
5  *
6  * This file is part of TUD:OS and distributed under the terms of the
7  * GNU General Public License 2.
8  * Please see the COPYING-GPL-2 file for details.
9  */
10 #pragma once
11
12 #include "hw_device.h"
13
14
15 class Pci_bridge;
16 namespace Hw { namespace Pci {
17
18   enum Cfg_width
19   {
20     Cfg_long  = 2,
21     Cfg_short = 1,
22     Cfg_byte  = 0,
23   };
24
25   inline Cfg_width cfg_w_to_o(int width)
26   {
27     switch (width)
28       {
29       default:
30       case 32: return Cfg_long;
31       case 16: return Cfg_short;
32       case 8:  return Cfg_byte;
33       }
34   }
35
36   template< Cfg_width w > struct Cfg_type;
37   template<> struct Cfg_type<Cfg_byte>  { typedef l4_uint8_t Type; };
38   template<> struct Cfg_type<Cfg_short> { typedef l4_uint16_t Type; };
39   template<> struct Cfg_type<Cfg_long>  { typedef l4_uint32_t Type; };
40
41   class If : public virtual Dev_if, public Dev_feature
42   {
43   public:
44
45     virtual Adr_resource *bar(int) const = 0;
46     virtual Adr_resource *rom() const = 0;
47     virtual bool is_64bit_high_bar(int) const = 0;
48     virtual bool supports_msi() const = 0;
49     virtual bool is_bridge() const = 0;
50
51     virtual int cfg_read(l4_uint32_t reg, l4_uint32_t *value, Cfg_width) = 0;
52     virtual int cfg_write(l4_uint32_t reg, l4_uint32_t value, Cfg_width) = 0;
53     virtual l4_uint32_t const *cfg_word(unsigned w) const = 0;
54     virtual unsigned bus_nr() const = 0;
55     virtual Pci_bridge *bus() const = 0;
56
57     virtual ~If() {}
58   };
59 }}
60
61
62
63
64 class Pci_dev : public virtual Hw::Pci::If, public Hw::Discover_res_if
65 {
66 protected:
67   Hw::Device *_host;
68   Pci_bridge *_bus;
69
70 public:
71   l4_uint32_t vendor_device;
72   l4_uint32_t cls_rev;
73   l4_uint32_t subsys_ids;
74   l4_uint8_t  hdr_type;
75   l4_uint8_t  irq_pin;
76   l4_uint16_t flags;
77
78 private:
79   Adr_resource *_bars[6];
80   Adr_resource *_rom;
81
82
83 public:
84
85   typedef Hw::Pci::Cfg_width Cfg_width;
86
87   enum Flags
88   {
89     F_msi = 1
90   };
91
92   enum Cfg_regs
93   {
94     /* Header type 0 config, normal PCI devices */
95     C_vendor         = 0x00,
96     C_device         = 0x02,
97     C_command        = 0x04,
98     C_status         = 0x06,
99     C_class_rev      = 0x08,
100     C_cacheline_size = 0x0c,
101     C_latency_timer  = 0x0d,
102     C_header_type    = 0x0e,
103     C_BIST           = 0x0f,
104     C_bar_0          = 0x10,
105     C_cardbus_cis    = 0x28,
106     C_subsys_vendor  = 0x2c,
107     C_subsys         = 0x2e,
108     C_rom_address    = 0x30,
109     C_capability_ptr = 0x34,
110     C_irq_line       = 0x3c,
111     C_irq_pin        = 0x3d,
112     C_min_gnt        = 0x3e,
113     C_max_lat        = 0x3f,
114
115     /* Header type 1, PCI-PCI bridges */
116     C_primary           = 0x18,
117     C_secondary         = 0x19,
118     C_subordinate       = 0x1a,
119     C_secondary_latency = 0x1b,
120     C_io_base           = 0x1c,
121     C_io_limit          = 0x1d,
122     C_secondary_status  = 0x1e,
123     C_mem_base          = 0x20,
124     C_mem_limit         = 0x22,
125     C_pref_mem_base     = 0x24,
126     C_pref_mem_limit    = 0x26,
127     C_pref_mem_base_hi  = 0x28,
128     C_pref_mem_limit_hi = 0x2c,
129     C_io_base_hi        = 0x30,
130     C_io_limit_hi       = 0x32,
131     C_rom_address_1     = 0x38,
132     C_bridge_control    = 0x3e,
133
134     /* header type 2, cardbus bridge */
135     C_cb_capability_ptr   = 0x14,
136     C_cb_secondary_status = 0x16,
137     C_cb_primary          = 0x18,
138     C_cb_cardbus          = 0x19,
139     C_cb_subordinate      = 0x1a,
140     C_cb_latency_timer    = 0x1b,
141     C_cb_mem_base_0       = 0x1c,
142     C_cb_mem_limit_0      = 0x20,
143     C_cb_mem_base_1       = 0x24,
144     C_cb_mem_limit_1      = 0x28,
145     C_cb_io_base_0        = 0x2c,
146     C_cb_io_base_0_hi     = 0x2e,
147     C_cb_io_limit_0       = 0x30,
148     C_cb_io_limit_0_hi    = 0x32,
149     C_cb_io_base_1        = 0x34,
150     C_cb_io_base_1_hi     = 0x36,
151     C_cb_io_limit_1       = 0x38,
152     C_cb_io_limi_1_hi     = 0x3a,
153     C_cb_bridge_control   = 0x3e,
154     C_cb_subsystem_vendor = 0x40,
155     C_cb_subsystem        = 0x42,
156     C_cb_legacy_mode_base = 0x44,
157
158   };
159
160   enum Cfg_status
161   {
162     CS_cap_list = 0x10,
163     CS_66_mhz   = 0x20,
164     CS_sig_target_abort = 0x0800,
165     CS_rec_target_abort = 0x1000,
166     CS_rec_master_abort = 0x2000,
167     CS_sig_system_error = 0x4000,
168     CS_detected_parity_error = 0x8000,
169   };
170
171   enum Cfg_command
172   {
173     CC_io          = 0x0001,
174     CC_mem         = 0x0002,
175     CC_bus_master  = 0x0004,
176     CC_serr        = 0x0100,
177     CC_int_disable = 0x0400,
178   };
179
180
181
182   Adr_resource *bar(int b) const
183   {
184     if (is_64bit_high_bar(b))
185       return _bars[b-1];
186     else
187       return _bars[b];
188   }
189
190   Adr_resource *rom() const
191   { return _rom; }
192
193   bool is_64bit_high_bar(int b) const
194   {
195     return l4_addr_t(_bars[b]) == 1;
196   }
197
198   explicit Pci_dev(Hw::Device *host, Pci_bridge *bus)
199   : _host(host), _bus(bus), vendor_device(0), cls_rev(0), flags(0), _rom(0)
200   {
201     for (unsigned i = 0; i < sizeof(_bars)/sizeof(_bars[0]); ++i)
202       _bars[i] = 0;
203   }
204
205   Hw::Device *host() const { return _host; }
206
207   bool supports_msi() const { return flags & F_msi; }
208
209   bool is_bridge() const
210   { return (cls_rev >> 16) == 0x0604 && (hdr_type & 0x7f) == 1; }
211
212   int cfg_read(l4_uint32_t reg, l4_uint32_t *value, Cfg_width);
213   int cfg_write(l4_uint32_t reg, l4_uint32_t value, Cfg_width);
214   l4_uint32_t const *cfg_word(unsigned w) const;
215   unsigned bus_nr() const;
216   Pci_bridge *bus() const { return _bus; }
217
218   void setup_resources(Hw::Device *host);
219   void discover_resources(Hw::Device *host);
220
221   bool match_cid(cxx::String const &cid) const;
222   void dump(int indent) const;
223
224   int vendor() const { return vendor_device & 0xffff; }
225   int device() const { return (vendor_device >> 16) & 0xffff; }
226
227   unsigned function_nr() const { return _host->adr() & 0x07; }
228   unsigned device_nr() const { return (_host->adr() >> 16) & 0x1f; }
229
230   unsigned disable_decoders();
231   void restore_decoders(unsigned cmd);
232
233 private:
234   int discover_bar(int bar);
235   void discover_expansion_rom();
236   void discover_legacy_ide_resources();
237   void discover_pci_caps();
238
239   void quirk_8086_8108();
240 };
241
242 class Pci_root_bridge;
243
244 class Pci_irq_router : public Resource
245 {
246 public:
247   Pci_irq_router() : Resource(Irq_res) {}
248   void dump(int) const;
249   bool compatible(Resource *consumer, bool = true) const
250   {
251     // only relative CPU IRQ lines are compatible with IRQ routing
252     // global IRQs must be allocated at a higher level
253     return consumer->type() == Irq_res && consumer->flags() & F_relative;
254   }
255
256 };
257
258 template< typename RES_SPACE >
259 class Pci_irq_router_res : public Pci_irq_router
260 {
261 protected:
262   typedef RES_SPACE Irq_rs;
263   mutable Irq_rs _rs;
264
265 public:
266   RES_SPACE *provided() const { return &_rs; }
267 };
268
269
270 class Pci_pci_bridge_irq_router_rs : public Resource_space
271 {
272 public:
273   bool request(Resource *parent, Device *, Resource *child, Device *cdev);
274   bool alloc(Resource *, Device *, Resource *, Device *, bool)
275   { return false; }
276 };
277
278
279 class Pci_bridge : public virtual Hw::Discover_bus_if
280 {
281 public:
282   typedef Hw::Pci::Cfg_width Cfg_width;
283
284   unsigned char num;
285   unsigned char subordinate;
286
287   explicit Pci_bridge(unsigned char bus) : num(bus), subordinate(bus) {}
288
289   virtual int cfg_read(unsigned bus, l4_uint32_t devfn,
290                        l4_uint32_t reg, l4_uint32_t *value, Cfg_width) = 0;
291   virtual int cfg_write(unsigned bus, l4_uint32_t devfn,
292                         l4_uint32_t reg, l4_uint32_t value, Cfg_width) = 0;
293
294   void scan_bus();
295   void dump(int) const;
296
297   virtual void increase_subordinate(int s) = 0;
298
299   virtual ~Pci_bridge() {}
300 };
301
302 class Pci_pci_bridge_basic : public Pci_bridge, public Pci_dev
303 {
304 public:
305   typedef Pci_dev::Cfg_width Cfg_width;
306
307   unsigned char pri;
308
309   using Pci_dev::cfg_write;
310   using Pci_dev::cfg_read;
311   using Pci_bridge::cfg_write;
312   using Pci_bridge::cfg_read;
313
314   explicit Pci_pci_bridge_basic(Hw::Device *host, Pci_bridge *bus)
315   : Pci_bridge(0), Pci_dev(host, bus), pri(0)
316   {}
317
318   void increase_subordinate(int x)
319   {
320     if (subordinate < x)
321       {
322         subordinate = x;
323         cfg_write(Pci_dev::C_subordinate, x, Hw::Pci::Cfg_byte);
324         _bus->increase_subordinate(x);
325       }
326   }
327
328   int cfg_read(unsigned bus, l4_uint32_t devfn,
329                l4_uint32_t reg, l4_uint32_t *value, Cfg_width width)
330   { return _bus->cfg_read(bus, devfn, reg, value, width); }
331
332   int cfg_write(unsigned bus, l4_uint32_t devfn,
333                 l4_uint32_t reg, l4_uint32_t value, Cfg_width width)
334   { return _bus->cfg_write(bus, devfn, reg, value, width); }
335
336   void dump(int indent) const
337   {
338     Pci_dev::dump(indent);
339     Pci_bridge::dump(indent);
340   }
341
342 };
343
344
345 class Pci_pci_bridge : public Pci_pci_bridge_basic
346 {
347 public:
348   Adr_resource *mmio;
349   Adr_resource *pref_mmio;
350   Adr_resource *io;
351
352   explicit Pci_pci_bridge(Hw::Device *host, Pci_bridge *bus)
353   : Pci_pci_bridge_basic(host, bus), mmio(0), pref_mmio(0), io(0)
354   {}
355
356   void setup_resources(Hw::Device *host);
357   void discover_resources(Hw::Device *host);
358 };
359
360 class Pci_root_bridge : public Pci_bridge
361 {
362 private:
363   Hw::Device *_host;
364
365 public:
366   explicit Pci_root_bridge(unsigned bus_nr, Hw::Device *host)
367   : Pci_bridge(bus_nr), _host(host)
368   {}
369
370
371   void set_host(Hw::Device *host) { _host = host; }
372
373   Hw::Device *host() const { return _host; }
374
375   void discover_resources(Hw::Device *host);
376   void setup_resources(Hw::Device *host);
377   void increase_subordinate(int x)
378   {
379     if (x > subordinate)
380       subordinate = x;
381   }
382 };
383
384 struct Pci_port_root_bridge : public Pci_root_bridge
385 {
386   explicit Pci_port_root_bridge(unsigned bus_nr, Hw::Device *host)
387   : Pci_root_bridge(bus_nr, host) {}
388
389   int cfg_read(unsigned  bus, l4_uint32_t devfn, l4_uint32_t reg,
390                l4_uint32_t *value, Cfg_width);
391
392   int cfg_write(unsigned bus, l4_uint32_t devfn, l4_uint32_t reg,
393                 l4_uint32_t value, Cfg_width);
394 };
395
396 Pci_root_bridge *pci_root_bridge(int segment);
397 int pci_register_root_bridge(int segment, Pci_root_bridge *b);
398
399
400 // IMPLEMENTATION ------------------------------------------------------
401
402 inline
403 int
404 Pci_dev::cfg_read(l4_uint32_t reg, l4_uint32_t *value, Cfg_width width)
405 {
406   return _bus->cfg_read(_bus->num, _host->adr(), reg, value, width);
407 }
408
409 inline
410 int
411 Pci_dev::cfg_write(l4_uint32_t reg, l4_uint32_t value, Cfg_width width)
412 {
413   return _bus->cfg_write(_bus->num, _host->adr(), reg, value, width);
414 }
415