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.
11 #include <l4/sys/types.h>
12 #include <l4/cxx/string>
13 #include <l4/cxx/minmax>
14 #include <l4/io/pciids.h>
21 #include "phys_space.h"
24 static Pci_root_bridge *__pci_root_bridge;
25 static unsigned _last_msi;
27 class Pci_cardbus_bridge : public Pci_pci_bridge_basic
30 Pci_cardbus_bridge(Hw::Device *host, Pci_bridge *bus)
31 : Pci_pci_bridge_basic(host, bus)
34 void discover_resources(Hw::Device *host);
37 Pci_root_bridge *pci_root_bridge(int segment)
41 return __pci_root_bridge;
44 #if defined(ARCH_x86) || defined(ARCH_amd64)
46 #include <l4/util/port_io.h>
49 pci_register_root_bridge(int segment, Pci_root_bridge *b)
54 __pci_root_bridge = b;
60 pci_conf_addr(l4_uint32_t bus, l4_uint32_t dev, l4_uint32_t fn, l4_uint32_t reg)
61 { return 0x80000000 | (bus << 16) | (dev << 11) | (fn << 8) | (reg & ~3); }
64 Pci_port_root_bridge::cfg_read(unsigned bus, l4_uint32_t devfn,
66 l4_uint32_t *value, Cfg_width order)
68 l4util_out32(pci_conf_addr(bus, devfn >> 16, devfn & 0xffff, reg), 0xcf8);
69 using namespace Hw::Pci;
73 case Cfg_byte: *value = l4util_in8(0xcfc + (reg & 3)); break;
74 case Cfg_short: *value = l4util_in16((0xcfc + (reg & 3)) & ~1UL); break;
75 case Cfg_long: *value = l4util_in32(0xcfc); break;
81 Pci_port_root_bridge::cfg_write(unsigned bus, l4_uint32_t devfn,
83 l4_uint32_t value, Cfg_width order)
85 l4util_out32(pci_conf_addr(bus, devfn >> 16, devfn & 0xffff, reg), 0xcf8);
86 using namespace Hw::Pci;
90 case Cfg_byte: l4util_out8(value, 0xcfc + (reg & 3)); break;
91 case Cfg_short: l4util_out16(value, (0xcfc + (reg & 3)) & ~1UL); break;
92 case Cfg_long: l4util_out32(value, 0xcfc); break;
100 static inline l4_uint32_t
101 devfn(unsigned dev, unsigned fn)
102 { return (dev << 16) | fn; }
107 Pci_bridge::scan_bus()
109 using namespace Hw::Pci;
112 for (device = 0; device < 32; ++device)
114 l4_uint32_t hdr_type;
115 cfg_read(num, devfn(device, 0), Pci_dev::C_header_type, &hdr_type, Cfg_byte);
116 int funcs = (hdr_type & 0x80) ? 8 : 1;
117 for (int function = 0; function < funcs; ++function)
119 l4_uint32_t vendor, _class;
120 cfg_read(num, devfn(device, function), Pci_dev::C_vendor, &vendor, Cfg_short);
121 if (vendor == 0xffff)
129 cfg_read(num, devfn(device, function), Pci_dev::C_vendor, &vendor, Cfg_long);
130 cfg_read(num, devfn(device, function), Pci_dev::C_class_rev, &_class, Cfg_long);
133 cfg_read(num, devfn(device, function), Pci_dev::C_header_type, &hdr_type, Cfg_byte);
135 Hw::Device *child = host()->get_child_dev_adr(devfn(device, function), true);
138 if ((_class >> 16) == 0x0604 || (_class >> 16) == 0x0607)
140 Pci_pci_bridge_basic *b = 0;
141 if ((hdr_type & 0x7f) == 1)
142 b = new Pci_pci_bridge(child, this);
143 else if((hdr_type & 0x7f) == 2)
144 b = new Pci_cardbus_bridge(child, this);
146 child->set_discover_bus_if(b);
149 bool reassign_busses = false;
151 cfg_read(num, devfn(device, function), Pci_dev::C_primary, &busses, Cfg_long);
153 if ((busses & 0xff) != num
154 || ((busses >> 8) & 0xff) <= num)
155 reassign_busses = true;
159 unsigned new_so = subordinate + 1;
162 b->subordinate = b->num;
164 busses = (busses & 0xff000000)
165 | ((l4_uint32_t)(b->pri))
166 | ((l4_uint32_t)(b->num) << 8)
167 | ((l4_uint32_t)(b->subordinate) << 16);
169 cfg_write(num, devfn(device, function), Pci_dev::C_primary, busses, Cfg_long);
170 increase_subordinate(new_so);
174 b->pri = busses & 0xff;
175 b->num = (busses >> 8) & 0xff;
176 b->subordinate = (busses >> 16) & 0xff;
182 d = new Pci_dev(child, this);
184 child->add_feature(d);
185 child->add_resource_discoverer(d);
187 d->vendor_device = vendor;
189 d->hdr_type = hdr_type;
201 Base_lo = 0xfee00000,
206 Dest_mode_phys = 0 << 2,
207 Dest_mode_log = 1 << 2,
212 Dest_redir_cpu = 0 << 3,
213 Dest_redir_lowprio = 1 << 3,
218 Data_del_fixed = 0 << 8,
219 Data_del_lowprio = 1 << 8,
224 Data_level_deassert = 0 << 14,
225 Data_level_assert = 1 << 14,
230 Data_trigger_edge = 0 << 15,
231 Data_trigger_level = 1 << 15,
234 static unsigned data(int vector) { return vector & 0xff; }
238 Pci_dev::cfg_word(unsigned w) const
243 case 0: return &vendor_device;
244 case 2: return &cls_rev;
245 case 11: return &subsys_ids;
250 Pci_dev::bus_nr() const
251 { return _bus->num; }
254 Pci_dev::disable_decoders()
258 cfg_read(C_command, &v, Hw::Pci::Cfg_byte);
259 cfg_write(C_command, v & ~3, Hw::Pci::Cfg_byte);
264 Pci_dev::restore_decoders(unsigned cmd)
266 cfg_write(C_command, cmd, Hw::Pci::Cfg_byte);
270 Pci_dev::discover_bar(int bar)
272 using namespace Hw::Pci;
276 int r = C_bar_0 + bar * 4;
277 l4_uint8_t cmd = disable_decoders();
279 cfg_read(r, &v, Cfg_long);
280 cfg_write(r, ~0U, Cfg_long);
281 cfg_read(r, &x, Cfg_long);
282 cfg_write(r, v, Cfg_long);
284 restore_decoders(cmd);
289 unsigned io_flags = (cmd & CC_io) ? 0 : Resource::F_disabled;
290 unsigned mem_flags = (cmd & CC_mem) ? 0 : Resource::F_disabled;
292 io_flags |= Resource::Io_res
293 | Resource::F_size_aligned
294 | Resource::F_hierarchical;
296 mem_flags |= Resource::Mmio_res
297 | Resource::F_size_aligned
298 | Resource::F_hierarchical;
300 Adr_resource *res = 0;
303 //printf("%08x: BAR[%d] mmio ... %x\n", adr(), bar, x );
304 res = new Adr_resource(mem_flags);
305 if ((x & 0x6) == 0x4)
306 res->add_flags(Adr_resource::F_width_64bit);
309 res->add_flags(Resource::F_prefetchable);
312 l4_uint64_t size = x & ~0x7f;
313 l4_uint64_t a = v & ~0x7f;
318 cmd = disable_decoders();
319 cfg_read(r, &v, Cfg_long);
320 cfg_write(r, ~0U, Cfg_long);
321 cfg_read(r, &x, Cfg_long);
322 cfg_write(r, v, Cfg_long);
323 restore_decoders(cmd);
324 a |= l4_uint64_t(v) << 32;
325 size |= l4_uint64_t(x) << 32;
328 for (int s = 7; s < 64; ++s)
336 res->start_size(a, size);
338 // printf("%08x: BAR[%d] mem ...\n", adr(), bar*4 + 10 );
339 _bars[bar - res->is_64bit()] = res;
341 _bars[bar] = (Adr_resource*)1;
346 // printf("%08x: BAR[%d] io ...\n", adr(), bar );
348 for (s = 2; s < 32; ++s)
352 res = new Adr_resource(io_flags);
355 res->start_size(v & ~3, 1 << s);
359 _host->add_resource(res);
364 Pci_dev::discover_legacy_ide_resources()
367 if (!Io_config::cfg->legacy_ide_resources(_host))
370 unsigned io_flags = Resource::Io_res
371 | Resource::F_size_aligned
372 | Resource::F_hierarchical;
374 // IDE legacy IO interface
375 if (cls_rev >> 16 == 0x101 && !(cls_rev & 0x100))
377 _host->add_resource(new Adr_resource(io_flags, 0x1f0, 0x1f7));
378 _host->add_resource(new Adr_resource(io_flags, 0x3f6, 0x3f6));
379 _host->add_resource(new Adr_resource(Resource::Irq_res | Resource::Irq_edge, 14, 14));
381 if (cls_rev >> 16 == 0x101 && !(cls_rev & 0x400))
383 _host->add_resource(new Adr_resource(io_flags, 0x170, 0x177));
384 _host->add_resource(new Adr_resource(io_flags, 0x376, 0x376));
385 _host->add_resource(new Adr_resource(Resource::Irq_res | Resource::Irq_edge, 15, 15));
390 Pci_dev::quirk_8086_8108()
392 using namespace Hw::Pci;
394 if (!(vendor() == 0x8086 && device() == 0x8108))
399 // GC - Graphics Control
400 cfg_read(0x52, &v, Cfg_short);
402 unsigned gfx_mem_sz = 0;
408 switch ((v >> 4) & 7)
410 case 1: gfx_mem_sz = 1 << 20; break;
411 case 2: gfx_mem_sz = 4 << 20; break;
412 case 3: gfx_mem_sz = 8 << 20; break;
416 // BSM - Base of Stolen Memory
417 cfg_read(0x5c, &v, Cfg_long);
420 unsigned flags = Resource::Mmio_res
421 | Resource::F_prefetchable
422 | Resource::F_fixed_addr
423 | Resource::F_fixed_size;
424 l4_addr_t end = v + gfx_mem_sz - 1;
426 _host->add_resource(new Adr_resource(flags, v, end));
427 for (Pci_bridge *p = _bus; p;)
429 p->host()->add_resource(new Adr_resource_provider(flags, v, end));
430 if (Pci_dev *d = dynamic_cast<Pci_dev *>(p))
438 Pci_dev::discover_expansion_rom()
440 using namespace Hw::Pci;
442 if (!Io_config::cfg->expansion_rom(host()))
446 unsigned rom_register = ((hdr_type & 0x7f) == 0) ? 12 * 4 : 14 * 4;
448 cfg_read(rom_register, &v, Cfg_long);
450 if (v == 0xffffffff || v == 0)
451 return; // no expansion ROM
453 cfg_write(rom_register, 0xfffffffe, Cfg_long);
454 cfg_read(rom_register, &x, Cfg_long);
455 cfg_write(rom_register, v, Cfg_long);
460 //printf("ROM %08x: %08x %08x\n", adr(), x, v);
463 for (s = 2; s < 32; ++s)
467 if (0) // currently we do not add a resource record for ROMs
469 unsigned flags = Adr_resource::Mmio_res | Adr_resource::F_size_aligned
470 | Adr_resource::F_rom | Adr_resource::F_prefetchable;
471 Adr_resource *res = new Adr_resource(flags);
474 res->start_size(v & ~3, 1 << s);
476 _host->add_resource(res);
481 Pci_dev::discover_pci_caps()
483 using namespace Hw::Pci;
487 cfg_read(C_status, &status, Cfg_short);
488 if (!(status & CS_cap_list))
493 cfg_read(C_capability_ptr, &cap_ptr, Cfg_byte);
495 for (; cap_ptr; cfg_read(cap_ptr + 1, &cap_ptr, Cfg_byte), cap_ptr &= ~0x3)
498 cfg_read(cap_ptr, &id, Cfg_byte);
499 //printf(" PCI-cap: %x\n", id);
500 if (id == 0x05 && Io_config::cfg->transparent_msi(host())
501 && system_icu()->info.supports_msi())
504 l4_uint32_t t, ctl, data;
505 l4_uint64_t addr = 0;
508 cfg_read(cap_ptr + 2, &ctl, Cfg_short);
509 cfg_read(cap_ptr + 4, &t, Cfg_long);
513 cfg_read(cap_ptr + 8, &t, Cfg_long);
514 addr |= ((l4_uint64_t)t) << 32;
518 cfg_read(cap_ptr + msg_offs, &data, Cfg_short);
520 cfg_write(cap_ptr + 4, Msi::Base_lo | Msi::Dest_mode_phys | Msi::Dest_redir_cpu, Cfg_long);
523 cfg_write(cap_ptr + 8, Msi::Base_hi, Cfg_long);
525 unsigned msi = _last_msi++;
526 if (msi >= system_icu()->info.nr_msis)
528 printf("WARNING: run out of MSI vectors, use normal IRQ\n");
533 if (l4_error(system_icu()->icu->msi_info(msi, &msg)) < 0)
535 printf("WARNING: could not get MSI message, use normal IRQ\n");
539 cfg_write(cap_ptr + msg_offs, Msi::Data_level_assert | Msi::Data_trigger_edge | Msi::Data_del_fixed | Msi::data(msg), Cfg_short);
541 cfg_write(cap_ptr + 2, ctl | 1, Cfg_short);
543 printf(" MSI cap: %x %llx %x\n", ctl, addr, data);
545 Adr_resource *res = new Adr_resource(Resource::Irq_res | Resource::Irq_edge, msi, msi);
547 _host->add_resource(res);
553 Pci_dev::discover_resources(Hw::Device *host)
555 using namespace Hw::Pci;
557 // printf("survey ... %x.%x\n", dynamic_cast<Pci_bridge*>(parent())->num, adr());
559 cfg_read(C_subsys_vendor, &v, Cfg_long);
561 cfg_read(C_irq_pin, &v, Cfg_byte);
565 host->add_resource(new Adr_resource(Resource::Irq_res | Resource::F_relative
566 | Resource::F_hierarchical,
567 irq_pin - 1, irq_pin - 1));
569 cfg_read(C_command, &v, Cfg_short);
571 int bars = ((hdr_type & 0x7f) == 0) ? 6 : 2;
573 discover_legacy_ide_resources();
577 for (int bar = 0; bar < bars;)
578 bar = discover_bar(bar);
580 discover_expansion_rom();
585 Pci_dev::setup_resources(Hw::Device *)
587 using namespace Hw::Pci;
589 for (unsigned i = 0; i < sizeof(_bars)/sizeof(_bars[0]); ++i)
591 Adr_resource *r = bar(i);
592 if (!r || r->type() == Resource::Io_res)
598 int reg = 0x10 + i * 4;
599 l4_uint64_t s = r->start();
600 l4_uint8_t cmd = disable_decoders();
601 cfg_write(reg, s, Cfg_long);
604 cfg_write(reg + 4, s >> 32, Cfg_long);
607 restore_decoders(cmd);
611 cfg_read(reg, &v, Cfg_long);
612 if (l4_uint32_t(v & ~0x7f) != l4_uint32_t(s & 0xffffffff))
613 printf("ERROR: could not set PCI BAR %d\n", i);
615 // printf("%08x: set BAR[%d] to %08x\n", adr(), i, v);
621 Pci_dev::match_cid(cxx::String const &_cid) const
623 cxx::String const prefix("PCI/");
624 cxx::String cid(_cid);
625 if (!cid.starts_with(prefix))
628 cid = cid.substr(prefix.len());
629 cxx::String::Index n;
630 for (; cid.len() > 0; cid = cid.substr(n + 1))
633 cxx::String tok = cid.head(n);
637 if (tok.starts_with("CC_"))
644 int l = tok.from_hex(&_csr);
645 if (l < 0 || l > 6 || l % 2)
648 if ((cls_rev >> (8 + (6-l) * 4)) == _csr)
653 else if (tok.starts_with("REV_"))
657 if (tok.len() != 2 || tok.from_hex(&r) != 2)
660 if (r != (cls_rev & 0xff))
663 else if (tok.starts_with("VEN_"))
667 if (tok.len() != 4 || tok.from_hex(&v) != 4)
670 if (((vendor_device >> 16) & 0xffff) != v)
673 else if (tok.starts_with("DEV_"))
677 if (tok.len() != 4 || tok.from_hex(&d) != 4)
680 if ((vendor_device & 0xffff) != d)
683 else if (tok.starts_with("SUBSYS_"))
687 if (tok.len() != 8 || tok.from_hex(&s) != 8)
700 Pci_pci_bridge::setup_resources(Hw::Device *host)
702 using namespace Hw::Pci;
704 Pci_dev::setup_resources(host);
706 if (!mmio->empty() && mmio->valid())
708 l4_uint32_t v = (mmio->start() >> 16) & 0xfff0;
709 v |= mmio->end() & 0xfff00000;
710 cfg_write(0x20, v, Cfg_long);
711 // printf("%08x: set mmio to %08x\n", adr(), v);
713 cfg_read(0x20, &r, Cfg_long);
714 // printf("%08x: mmio = %08x\n", adr(), r);
715 cfg_read(0x04, &r, Cfg_short);
717 cfg_write(0x4, r, Cfg_short);
720 if (!pref_mmio->empty() && pref_mmio->valid())
722 l4_uint32_t v = (pref_mmio->start() >> 16) & 0xfff0;
723 v |= pref_mmio->end() & 0xfff00000;
724 cfg_write(0x24, v, Cfg_long);
725 // printf("%08x: set pref mmio to %08x\n", adr(), v);
730 Pci_pci_bridge::discover_resources(Hw::Device *host)
732 using namespace Hw::Pci;
737 cfg_read(C_mem_base, &v, Cfg_long);
738 s = (v & 0xfff0) << 16;
739 e = (v & 0xfff00000) | 0xfffff;
741 Adr_resource *r = new Adr_resource_provider(Resource::Mmio_res);
742 r->alignment(0xfffff);
748 // printf("%08x: mmio = %08x\n", adr(), v);
751 _host->add_resource(mmio);
753 r = new Adr_resource_provider(Resource::Mmio_res | Resource::F_prefetchable);
754 cfg_read(C_pref_mem_base, &v, Cfg_long);
755 s = (v & 0xfff0) << 16;
756 e = (v & 0xfff00000) | 0xfffff;
760 r->add_flags(Adr_resource::F_width_64bit);
761 cfg_read(C_pref_mem_base_hi, &v, Cfg_long);
762 s |= l4_uint64_t(v) << 32;
763 cfg_read(C_pref_mem_limit_hi, &v, Cfg_long);
764 e |= l4_uint64_t(v) << 32;
767 r->alignment(0xfffff);
775 _host->add_resource(r);
777 cfg_read(C_io_base, &v, Cfg_short);
779 e = (v & 0xf000) | 0xfff;
781 r = new Adr_resource_provider(Resource::Io_res);
789 _host->add_resource(r);
791 Pci_dev::discover_resources(host);
796 Pci_root_bridge::discover_resources(Hw::Device *)
802 static const char * const pci_classes[] =
803 { "unknown", "mass storage contoller", "network controller",
804 "display controller", "multimedia device", "memory controller",
805 "bridge device", "simple communication controller",
806 "system peripheral", "input device", "docking station",
807 "processor", "serial bus controller", "wireless controller",
808 "intelligent I/O controller", "satellite communication controller",
809 "encryption/decryption controller",
810 "data aquisition/signal processing controller" };
812 static char const * const pci_bridges[] =
813 { "Host/PCI Bridge", "ISA Bridge", "EISA Bridge", "Micro Channel Bridge",
814 "PCI Bridge", "PCMCIA Bridge", "NuBus Bridge", "CardBus Bridge" };
818 dump_res_rec(Resource_list const *r, int indent)
820 for (Resource_list::iterator i = r->begin(); i!= r->end(); ++i)
824 //dump_res_rec(i->child(), indent + 2);
829 Pci_dev::dump(int indent) const
831 char const *classname = "";
833 if (cls_rev >> 24 < sizeof(pci_classes)/sizeof(pci_classes[0]))
834 classname = pci_classes[cls_rev >> 24];
836 if ((cls_rev >> 24) == 0x06)
838 unsigned sc = (cls_rev >> 16) & 0xff;
839 if (sc < sizeof(pci_bridges)/sizeof(pci_bridges[0]))
840 classname = pci_bridges[sc];
843 printf("%*.s%04x:%02x:%02x.%x: %s [%d]\n", indent, " ",
844 0, (int)_bus->num, _host->adr() >> 16, _host->adr() & 0xffff,
845 classname, (int)hdr_type);
848 printf("%*.s 0x%04x 0x%04x\n", indent, " ", vendor(), device());
849 libpciids_name_device(buf, sizeof(buf), vendor(), device());
850 printf("%*.s %s\n", indent, " ", buf);
853 dump_res_rec(resources(), 0);
858 Pci_bridge::dump(int) const
861 "bridge %04x:%02x:%02x.%x\n",
862 b->num, 0, b->parent() ? (int)static_cast<Pci_bridge*>(b->parent())->num : 0,
863 b->adr() >> 16, b->adr() & 0xffff);
866 //dump_res_rec(resources(), 0);
868 for (iterator c = begin(0); c != end(); ++c)
876 Pci_cardbus_bridge::discover_resources(Hw::Device *host)
878 using namespace Hw::Pci;
880 cfg_read(C_subsys_vendor, &v, Cfg_long);
883 Adr_resource *r = new Adr_resource_provider(Resource::Mmio_res);
884 cfg_read(C_cb_mem_base_0, &v, Cfg_long);
886 cfg_read(C_cb_mem_limit_0, &v, Cfg_long);
891 host->add_resource(r);
893 r = new Adr_resource_provider(Resource::Mmio_res);
894 cfg_read(C_cb_mem_base_1, &v, Cfg_long);
896 cfg_read(C_cb_mem_limit_1, &v, Cfg_long);
901 host->add_resource(r);
903 r = new Adr_resource_provider(Resource::Io_res);
904 cfg_read(C_cb_io_base_0, &v, Cfg_long);
906 cfg_read(C_cb_io_limit_0, &v, Cfg_long);
911 host->add_resource(r);
913 r = new Adr_resource_provider(Resource::Io_res);
914 cfg_read(C_cb_io_base_1, &v, Cfg_long);
916 cfg_read(C_cb_io_limit_1, &v, Cfg_long);
921 host->add_resource(r);
925 Pci_irq_router::dump(int indent) const
927 printf("%*sPCI IRQ ROUTER: %s (%p)\n", indent, "", typeid(*this).name(),
932 Pci_pci_bridge_irq_router_rs::request(Resource *parent, Device *pdev,
933 Resource *child, Device *cdev)
935 Adr_resource *cr = dynamic_cast<Adr_resource*>(child);
939 // assume we allocate an abstract IRQ router resource as a child
941 child->parent(parent);
947 Hw::Device *cd = dynamic_cast<Hw::Device*>(cdev);
954 cr->start((cr->start() + (cd->adr() >> 16)) & 3);
955 res = pdev->parent()->request_child_resource(cr, pdev);