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)
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.
12 #include "hw_device.h"
16 namespace Hw { namespace Pci {
25 inline Cfg_width cfg_w_to_o(int width)
30 case 32: return Cfg_long;
31 case 16: return Cfg_short;
32 case 8: return Cfg_byte;
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; };
41 class If : public virtual Dev_if, public Dev_feature
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;
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;
64 class Pci_dev : public virtual Hw::Pci::If, public Hw::Discover_res_if
71 l4_uint32_t vendor_device;
73 l4_uint32_t subsys_ids;
79 Adr_resource *_bars[6];
85 typedef Hw::Pci::Cfg_width Cfg_width;
94 /* Header type 0 config, normal PCI devices */
100 C_cacheline_size = 0x0c,
101 C_latency_timer = 0x0d,
102 C_header_type = 0x0e,
105 C_cardbus_cis = 0x28,
106 C_subsys_vendor = 0x2c,
108 C_rom_address = 0x30,
109 C_capability_ptr = 0x34,
115 /* Header type 1, PCI-PCI bridges */
118 C_subordinate = 0x1a,
119 C_secondary_latency = 0x1b,
122 C_secondary_status = 0x1e,
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,
130 C_io_limit_hi = 0x32,
131 C_rom_address_1 = 0x38,
132 C_bridge_control = 0x3e,
134 /* header type 2, cardbus bridge */
135 C_cb_capability_ptr = 0x14,
136 C_cb_secondary_status = 0x16,
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,
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,
175 CC_bus_master = 0x0004,
177 CC_int_disable = 0x0400,
182 Adr_resource *bar(int b) const
184 if (is_64bit_high_bar(b))
190 Adr_resource *rom() const
193 bool is_64bit_high_bar(int b) const
195 return l4_addr_t(_bars[b]) == 1;
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)
201 for (unsigned i = 0; i < sizeof(_bars)/sizeof(_bars[0]); ++i)
205 Hw::Device *host() const { return _host; }
207 bool supports_msi() const { return flags & F_msi; }
209 bool is_bridge() const
210 { return (cls_rev >> 16) == 0x0604 && (hdr_type & 0x7f) == 1; }
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; }
218 void setup_resources(Hw::Device *host);
219 void discover_resources(Hw::Device *host);
221 bool match_cid(cxx::String const &cid) const;
222 void dump(int indent) const;
224 int vendor() const { return vendor_device & 0xffff; }
225 int device() const { return (vendor_device >> 16) & 0xffff; }
227 unsigned function_nr() const { return _host->adr() & 0x07; }
228 unsigned device_nr() const { return (_host->adr() >> 16) & 0x1f; }
230 unsigned disable_decoders();
231 void restore_decoders(unsigned cmd);
234 int discover_bar(int bar);
235 void discover_expansion_rom();
236 void discover_legacy_ide_resources();
237 void discover_pci_caps();
239 void quirk_8086_8108();
242 class Pci_root_bridge;
244 class Pci_irq_router : public Resource
247 Pci_irq_router() : Resource(Irq_res) {}
248 void dump(int) const;
249 bool compatible(Resource *consumer, bool = true) const
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;
258 template< typename RES_SPACE >
259 class Pci_irq_router_res : public Pci_irq_router
262 typedef RES_SPACE Irq_rs;
266 RES_SPACE *provided() const { return &_rs; }
270 class Pci_pci_bridge_irq_router_rs : public Resource_space
273 bool request(Resource *parent, Device *, Resource *child, Device *cdev);
274 bool alloc(Resource *, Device *, Resource *, Device *, bool)
279 class Pci_bridge : public virtual Hw::Discover_bus_if
282 typedef Hw::Pci::Cfg_width Cfg_width;
285 unsigned char subordinate;
287 explicit Pci_bridge(unsigned char bus) : num(bus), subordinate(bus) {}
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;
295 void dump(int) const;
297 virtual void increase_subordinate(int s) = 0;
299 virtual ~Pci_bridge() {}
302 class Pci_pci_bridge_basic : public Pci_bridge, public Pci_dev
305 typedef Pci_dev::Cfg_width Cfg_width;
309 using Pci_dev::cfg_write;
310 using Pci_dev::cfg_read;
311 using Pci_bridge::cfg_write;
312 using Pci_bridge::cfg_read;
314 explicit Pci_pci_bridge_basic(Hw::Device *host, Pci_bridge *bus)
315 : Pci_bridge(0), Pci_dev(host, bus), pri(0)
318 void increase_subordinate(int x)
323 cfg_write(Pci_dev::C_subordinate, x, Hw::Pci::Cfg_byte);
324 _bus->increase_subordinate(x);
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); }
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); }
336 void dump(int indent) const
338 Pci_dev::dump(indent);
339 Pci_bridge::dump(indent);
345 class Pci_pci_bridge : public Pci_pci_bridge_basic
349 Adr_resource *pref_mmio;
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)
356 void setup_resources(Hw::Device *host);
357 void discover_resources(Hw::Device *host);
360 class Pci_root_bridge : public Pci_bridge
366 explicit Pci_root_bridge(unsigned bus_nr, Hw::Device *host)
367 : Pci_bridge(bus_nr), _host(host)
371 void set_host(Hw::Device *host) { _host = host; }
373 Hw::Device *host() const { return _host; }
375 void discover_resources(Hw::Device *host);
376 void setup_resources(Hw::Device *host);
377 void increase_subordinate(int x)
384 struct Pci_port_root_bridge : public Pci_root_bridge
386 explicit Pci_port_root_bridge(unsigned bus_nr, Hw::Device *host)
387 : Pci_root_bridge(bus_nr, host) {}
389 int cfg_read(unsigned bus, l4_uint32_t devfn, l4_uint32_t reg,
390 l4_uint32_t *value, Cfg_width);
392 int cfg_write(unsigned bus, l4_uint32_t devfn, l4_uint32_t reg,
393 l4_uint32_t value, Cfg_width);
396 Pci_root_bridge *pci_root_bridge(int segment);
397 int pci_register_root_bridge(int segment, Pci_root_bridge *b);
400 // IMPLEMENTATION ------------------------------------------------------
404 Pci_dev::cfg_read(l4_uint32_t reg, l4_uint32_t *value, Cfg_width width)
406 return _bus->cfg_read(_bus->num, _host->adr(), reg, value, width);
411 Pci_dev::cfg_write(l4_uint32_t reg, l4_uint32_t value, Cfg_width width)
413 return _bus->cfg_write(_bus->num, _host->adr(), reg, value, width);