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>
22 #include "phys_space.h"
26 static Pci_root_bridge *__pci_root_bridge;
27 static unsigned _last_msi;
29 class Pci_cardbus_bridge : public Pci_pci_bridge_basic
32 Pci_cardbus_bridge(Hw::Device *host, Pci_bridge *bus)
33 : Pci_pci_bridge_basic(host, bus)
36 void discover_resources(Hw::Device *host);
39 Pci_root_bridge *pci_root_bridge(int segment)
43 return __pci_root_bridge;
46 #if defined(ARCH_x86) || defined(ARCH_amd64)
48 #include <l4/util/port_io.h>
51 pci_register_root_bridge(int segment, Pci_root_bridge *b)
56 __pci_root_bridge = b;
62 pci_conf_addr(l4_uint32_t bus, l4_uint32_t dev, l4_uint32_t fn, l4_uint32_t reg)
63 { return 0x80000000 | (bus << 16) | (dev << 11) | (fn << 8) | (reg & ~3); }
66 Pci_port_root_bridge::cfg_read(unsigned bus, l4_uint32_t devfn,
68 l4_uint32_t *value, Cfg_width order)
70 l4util_out32(pci_conf_addr(bus, devfn >> 16, devfn & 0xffff, reg), 0xcf8);
71 using namespace Hw::Pci;
75 case Cfg_byte: *value = l4util_in8(0xcfc + (reg & 3)); break;
76 case Cfg_short: *value = l4util_in16((0xcfc + (reg & 3)) & ~1UL); break;
77 case Cfg_long: *value = l4util_in32(0xcfc); break;
83 Pci_port_root_bridge::cfg_write(unsigned bus, l4_uint32_t devfn,
85 l4_uint32_t value, Cfg_width order)
87 l4util_out32(pci_conf_addr(bus, devfn >> 16, devfn & 0xffff, reg), 0xcf8);
88 using namespace Hw::Pci;
92 case Cfg_byte: l4util_out8(value, 0xcfc + (reg & 3)); break;
93 case Cfg_short: l4util_out16(value, (0xcfc + (reg & 3)) & ~1UL); break;
94 case Cfg_long: l4util_out32(value, 0xcfc); break;
102 static inline l4_uint32_t
103 devfn(unsigned dev, unsigned fn)
104 { return (dev << 16) | fn; }
109 Pci_bridge::scan_bus()
111 using namespace Hw::Pci;
114 for (device = 0; device < 32; ++device)
116 l4_uint32_t hdr_type;
117 cfg_read(num, devfn(device, 0), Pci_dev::C_header_type, &hdr_type, Cfg_byte);
118 int funcs = (hdr_type & 0x80) ? 8 : 1;
119 for (int function = 0; function < funcs; ++function)
121 l4_uint32_t vendor, _class;
122 cfg_read(num, devfn(device, function), Pci_dev::C_vendor, &vendor, Cfg_short);
123 if (vendor == 0xffff)
131 cfg_read(num, devfn(device, function), Pci_dev::C_vendor, &vendor, Cfg_long);
132 cfg_read(num, devfn(device, function), Pci_dev::C_class_rev, &_class, Cfg_long);
135 cfg_read(num, devfn(device, function), Pci_dev::C_header_type, &hdr_type, Cfg_byte);
137 Hw::Device *child = host()->get_child_dev_adr(devfn(device, function), true);
140 if ((_class >> 16) == 0x0604 || (_class >> 16) == 0x0607)
142 Pci_pci_bridge_basic *b = 0;
143 if ((hdr_type & 0x7f) == 1)
144 b = new Pci_pci_bridge(child, this);
145 else if((hdr_type & 0x7f) == 2)
146 b = new Pci_cardbus_bridge(child, this);
148 child->set_discover_bus_if(b);
151 bool reassign_busses = false;
153 cfg_read(num, devfn(device, function), Pci_dev::C_primary, &busses, Cfg_long);
155 if ((busses & 0xff) != num
156 || ((busses >> 8) & 0xff) <= num)
157 reassign_busses = true;
161 unsigned new_so = subordinate + 1;
164 b->subordinate = b->num;
166 busses = (busses & 0xff000000)
167 | ((l4_uint32_t)(b->pri))
168 | ((l4_uint32_t)(b->num) << 8)
169 | ((l4_uint32_t)(b->subordinate) << 16);
171 cfg_write(num, devfn(device, function), Pci_dev::C_primary, busses, Cfg_long);
172 increase_subordinate(new_so);
176 b->pri = busses & 0xff;
177 b->num = (busses >> 8) & 0xff;
178 b->subordinate = (busses >> 16) & 0xff;
184 d = new Pci_dev(child, this);
186 child->add_feature(d);
187 child->add_resource_discoverer(d);
189 d->vendor_device = vendor;
191 d->hdr_type = hdr_type;
203 Base_lo = 0xfee00000,
208 Dest_mode_phys = 0 << 2,
209 Dest_mode_log = 1 << 2,
214 Dest_redir_cpu = 0 << 3,
215 Dest_redir_lowprio = 1 << 3,
220 Data_del_fixed = 0 << 8,
221 Data_del_lowprio = 1 << 8,
226 Data_level_deassert = 0 << 14,
227 Data_level_assert = 1 << 14,
232 Data_trigger_edge = 0 << 15,
233 Data_trigger_level = 1 << 15,
236 static unsigned data(int vector) { return vector & 0xff; }
240 Pci_dev::cfg_word(unsigned w) const
245 case 0: return &vendor_device;
246 case 2: return &cls_rev;
247 case 11: return &subsys_ids;
252 Pci_dev::bus_nr() const
253 { return _bus->num; }
256 Pci_dev::disable_decoders()
260 cfg_read(C_command, &v, Hw::Pci::Cfg_byte);
261 cfg_write(C_command, v & ~3, Hw::Pci::Cfg_byte);
266 Pci_dev::restore_decoders(unsigned cmd)
268 cfg_write(C_command, cmd, Hw::Pci::Cfg_byte);
272 Pci_dev::discover_bar(int bar)
274 using namespace Hw::Pci;
278 int r = C_bar_0 + bar * 4;
279 l4_uint8_t cmd = disable_decoders();
281 cfg_read(r, &v, Cfg_long);
282 cfg_write(r, ~0U, Cfg_long);
283 cfg_read(r, &x, Cfg_long);
284 cfg_write(r, v, Cfg_long);
286 restore_decoders(cmd);
291 unsigned io_flags = (cmd & CC_io) ? 0 : Resource::F_disabled;
292 unsigned mem_flags = (cmd & CC_mem) ? 0 : Resource::F_disabled;
294 io_flags |= Resource::Io_res
295 | Resource::F_size_aligned
296 | Resource::F_hierarchical;
298 mem_flags |= Resource::Mmio_res
299 | Resource::F_size_aligned
300 | Resource::F_hierarchical;
302 Adr_resource *res = 0;
305 //printf("%08x: BAR[%d] mmio ... %x\n", adr(), bar, x );
306 res = new Adr_resource(mem_flags);
307 if ((x & 0x6) == 0x4)
308 res->add_flags(Adr_resource::F_width_64bit);
311 res->add_flags(Resource::F_prefetchable);
314 l4_uint64_t size = x & ~0x7f;
315 l4_uint64_t a = v & ~0x7f;
320 cmd = disable_decoders();
321 cfg_read(r, &v, Cfg_long);
322 cfg_write(r, ~0U, Cfg_long);
323 cfg_read(r, &x, Cfg_long);
324 cfg_write(r, v, Cfg_long);
325 restore_decoders(cmd);
326 a |= l4_uint64_t(v) << 32;
327 size |= l4_uint64_t(x) << 32;
330 for (int s = 7; s < 64; ++s)
338 res->start_size(a, size);
340 // printf("%08x: BAR[%d] mem ...\n", adr(), bar*4 + 10 );
341 _bars[bar - res->is_64bit()] = res;
343 _bars[bar] = (Adr_resource*)1;
348 // printf("%08x: BAR[%d] io ...\n", adr(), bar );
350 for (s = 2; s < 32; ++s)
354 res = new Adr_resource(io_flags);
357 res->start_size(v & ~3, 1 << s);
361 _host->add_resource(res);
366 Pci_dev::discover_legacy_ide_resources()
369 if (!Io_config::cfg->legacy_ide_resources(_host))
372 unsigned io_flags = Resource::Io_res
373 | Resource::F_size_aligned
374 | Resource::F_hierarchical;
376 // IDE legacy IO interface
377 if (cls_rev >> 16 == 0x101 && !(cls_rev & 0x100))
379 _host->add_resource(new Adr_resource(io_flags, 0x1f0, 0x1f7));
380 _host->add_resource(new Adr_resource(io_flags, 0x3f6, 0x3f6));
381 _host->add_resource(new Adr_resource(Resource::Irq_res | Resource::Irq_edge, 14, 14));
383 if (cls_rev >> 16 == 0x101 && !(cls_rev & 0x400))
385 _host->add_resource(new Adr_resource(io_flags, 0x170, 0x177));
386 _host->add_resource(new Adr_resource(io_flags, 0x376, 0x376));
387 _host->add_resource(new Adr_resource(Resource::Irq_res | Resource::Irq_edge, 15, 15));
392 Pci_dev::quirk_8086_8108()
394 using namespace Hw::Pci;
396 if (!(vendor() == 0x8086 && device() == 0x8108))
401 // GC - Graphics Control
402 cfg_read(0x52, &v, Cfg_short);
404 unsigned gfx_mem_sz = 0;
410 switch ((v >> 4) & 7)
412 case 1: gfx_mem_sz = 1 << 20; break;
413 case 2: gfx_mem_sz = 4 << 20; break;
414 case 3: gfx_mem_sz = 8 << 20; break;
418 // BSM - Base of Stolen Memory
419 cfg_read(0x5c, &v, Cfg_long);
422 unsigned flags = Resource::Mmio_res
423 | Resource::F_prefetchable
424 | Resource::F_fixed_addr
425 | Resource::F_fixed_size;
426 l4_addr_t end = v + gfx_mem_sz - 1;
428 _host->add_resource(new Adr_resource(flags, v, end));
429 for (Pci_bridge *p = _bus; p;)
431 p->host()->add_resource(new Adr_resource_provider(flags, v, end));
432 if (Pci_dev *d = dynamic_cast<Pci_dev *>(p))
440 Pci_dev::discover_expansion_rom()
442 using namespace Hw::Pci;
444 if (!Io_config::cfg->expansion_rom(host()))
448 unsigned rom_register = ((hdr_type & 0x7f) == 0) ? 12 * 4 : 14 * 4;
450 cfg_read(rom_register, &v, Cfg_long);
452 if (v == 0xffffffff || v == 0)
453 return; // no expansion ROM
455 cfg_write(rom_register, 0xfffffffe, Cfg_long);
456 cfg_read(rom_register, &x, Cfg_long);
457 cfg_write(rom_register, v, Cfg_long);
462 //printf("ROM %08x: %08x %08x\n", adr(), x, v);
465 for (s = 2; s < 32; ++s)
469 if (0) // currently we do not add a resource record for ROMs
471 unsigned flags = Adr_resource::Mmio_res | Adr_resource::F_size_aligned
472 | Adr_resource::F_rom | Adr_resource::F_prefetchable;
473 Adr_resource *res = new Adr_resource(flags);
476 res->start_size(v & ~3, 1 << s);
478 _host->add_resource(res);
483 Pci_dev::discover_pci_caps()
485 using namespace Hw::Pci;
489 cfg_read(C_status, &status, Cfg_short);
490 if (!(status & CS_cap_list))
495 cfg_read(C_capability_ptr, &cap_ptr, Cfg_byte);
497 for (; cap_ptr; cfg_read(cap_ptr + 1, &cap_ptr, Cfg_byte), cap_ptr &= ~0x3)
500 cfg_read(cap_ptr, &id, Cfg_byte);
501 // printf(" PCI-cap: %x %s %s\n", id, Io_config::cfg->transparent_msi(host()) ? "yes" : "no", system_icu()->info.supports_msi() ? "yes" : "no" );
502 if (id == 0x05 && Io_config::cfg->transparent_msi(host())
503 && system_icu()->info.supports_msi())
506 l4_uint32_t t, ctl, data;
507 l4_uint64_t addr = 0;
510 cfg_read(cap_ptr + 2, &ctl, Cfg_short);
511 cfg_read(cap_ptr + 4, &t, Cfg_long);
515 cfg_read(cap_ptr + 8, &t, Cfg_long);
516 addr |= ((l4_uint64_t)t) << 32;
520 cfg_read(cap_ptr + msg_offs, &data, Cfg_short);
522 cfg_write(cap_ptr + 4, Msi::Base_lo | Msi::Dest_mode_phys | Msi::Dest_redir_cpu, Cfg_long);
525 cfg_write(cap_ptr + 8, Msi::Base_hi, Cfg_long);
527 unsigned msi = _last_msi++;
528 if (msi >= system_icu()->info.nr_msis)
530 d_printf(DBG_WARN, "WARNING: run out of MSI vectors, use normal IRQ\n");
535 if (l4_error(system_icu()->icu->msi_info(msi, &msg)) < 0)
537 d_printf(DBG_WARN, "WARNING: could not get MSI message, use normal IRQ\n");
541 cfg_write(cap_ptr + msg_offs, Msi::Data_level_assert | Msi::Data_trigger_edge | Msi::Data_del_fixed | Msi::data(msg), Cfg_short);
543 cfg_write(cap_ptr + 2, ctl | 1, Cfg_short);
545 d_printf(DBG_DEBUG2, " MSI cap: %x %llx %x\n", ctl, addr, data);
546 for (Resource_list::iterator i = host()->resources()->begin();
547 i != host()->resources()->end(); ++i)
549 if ((*i)->type() == Resource::Irq_res)
551 (*i)->set_empty(true);
552 (*i)->add_flags(Resource::F_disabled);
556 Adr_resource *res = new Hw::Msi_resource(msi);
558 _host->add_resource(res);
564 Pci_dev::discover_resources(Hw::Device *host)
566 using namespace Hw::Pci;
568 // printf("survey ... %x.%x\n", dynamic_cast<Pci_bridge*>(parent())->num, adr());
570 cfg_read(C_subsys_vendor, &v, Cfg_long);
572 cfg_read(C_irq_pin, &v, Cfg_byte);
576 host->add_resource(new Adr_resource(Resource::Irq_res | Resource::F_relative
577 | Resource::F_hierarchical,
578 irq_pin - 1, irq_pin - 1));
580 cfg_read(C_command, &v, Cfg_short);
582 int bars = ((hdr_type & 0x7f) == 0) ? 6 : 2;
584 discover_legacy_ide_resources();
588 for (int bar = 0; bar < bars;)
589 bar = discover_bar(bar);
591 discover_expansion_rom();
596 Pci_dev::setup_resources(Hw::Device *)
598 using namespace Hw::Pci;
600 for (unsigned i = 0; i < sizeof(_bars)/sizeof(_bars[0]); ++i)
602 Adr_resource *r = bar(i);
603 if (!r || r->type() == Resource::Io_res)
609 int reg = 0x10 + i * 4;
610 l4_uint64_t s = r->start();
611 l4_uint8_t cmd = disable_decoders();
612 cfg_write(reg, s, Cfg_long);
615 cfg_write(reg + 4, s >> 32, Cfg_long);
618 restore_decoders(cmd);
622 cfg_read(reg, &v, Cfg_long);
623 if (l4_uint32_t(v & ~0x7f) != l4_uint32_t(s & 0xffffffff))
624 d_printf(DBG_ERR, "ERROR: could not set PCI BAR %d\n", i);
626 // printf("%08x: set BAR[%d] to %08x\n", adr(), i, v);
632 Pci_dev::match_cid(cxx::String const &_cid) const
634 cxx::String const prefix("PCI/");
635 cxx::String cid(_cid);
636 if (!cid.starts_with(prefix))
639 cid = cid.substr(prefix.len());
640 cxx::String::Index n;
641 for (; cid.len() > 0; cid = cid.substr(n + 1))
644 cxx::String tok = cid.head(n);
648 if (tok.starts_with("CC_"))
655 int l = tok.from_hex(&_csr);
656 if (l < 0 || l > 6 || l % 2)
659 if ((cls_rev >> (8 + (6-l) * 4)) == _csr)
664 else if (tok.starts_with("REV_"))
668 if (tok.len() != 2 || tok.from_hex(&r) != 2)
671 if (r != (cls_rev & 0xff))
674 else if (tok.starts_with("VEN_"))
678 if (tok.len() != 4 || tok.from_hex(&v) != 4)
681 if (((vendor_device >> 16) & 0xffff) != v)
684 else if (tok.starts_with("DEV_"))
688 if (tok.len() != 4 || tok.from_hex(&d) != 4)
691 if ((vendor_device & 0xffff) != d)
694 else if (tok.starts_with("SUBSYS_"))
698 if (tok.len() != 8 || tok.from_hex(&s) != 8)
711 Pci_pci_bridge::setup_resources(Hw::Device *host)
713 using namespace Hw::Pci;
715 Pci_dev::setup_resources(host);
717 if (!mmio->empty() && mmio->valid())
719 l4_uint32_t v = (mmio->start() >> 16) & 0xfff0;
720 v |= mmio->end() & 0xfff00000;
721 cfg_write(0x20, v, Cfg_long);
722 // printf("%08x: set mmio to %08x\n", adr(), v);
724 cfg_read(0x20, &r, Cfg_long);
725 // printf("%08x: mmio = %08x\n", adr(), r);
726 cfg_read(0x04, &r, Cfg_short);
728 cfg_write(0x4, r, Cfg_short);
731 if (!pref_mmio->empty() && pref_mmio->valid())
733 l4_uint32_t v = (pref_mmio->start() >> 16) & 0xfff0;
734 v |= pref_mmio->end() & 0xfff00000;
735 cfg_write(0x24, v, Cfg_long);
736 // printf("%08x: set pref mmio to %08x\n", adr(), v);
741 Pci_pci_bridge::discover_resources(Hw::Device *host)
743 using namespace Hw::Pci;
748 cfg_read(C_mem_base, &v, Cfg_long);
749 s = (v & 0xfff0) << 16;
750 e = (v & 0xfff00000) | 0xfffff;
752 Adr_resource *r = new Adr_resource_provider(Resource::Mmio_res);
753 r->alignment(0xfffff);
759 // printf("%08x: mmio = %08x\n", adr(), v);
762 _host->add_resource(mmio);
764 r = new Adr_resource_provider(Resource::Mmio_res | Resource::F_prefetchable);
765 cfg_read(C_pref_mem_base, &v, Cfg_long);
766 s = (v & 0xfff0) << 16;
767 e = (v & 0xfff00000) | 0xfffff;
771 r->add_flags(Adr_resource::F_width_64bit);
772 cfg_read(C_pref_mem_base_hi, &v, Cfg_long);
773 s |= l4_uint64_t(v) << 32;
774 cfg_read(C_pref_mem_limit_hi, &v, Cfg_long);
775 e |= l4_uint64_t(v) << 32;
778 r->alignment(0xfffff);
786 _host->add_resource(r);
788 cfg_read(C_io_base, &v, Cfg_short);
790 e = (v & 0xf000) | 0xfff;
792 r = new Adr_resource_provider(Resource::Io_res);
800 _host->add_resource(r);
802 Pci_dev::discover_resources(host);
807 Pci_root_bridge::discover_resources(Hw::Device *)
813 static const char * const pci_classes[] =
814 { "unknown", "mass storage contoller", "network controller",
815 "display controller", "multimedia device", "memory controller",
816 "bridge device", "simple communication controller",
817 "system peripheral", "input device", "docking station",
818 "processor", "serial bus controller", "wireless controller",
819 "intelligent I/O controller", "satellite communication controller",
820 "encryption/decryption controller",
821 "data aquisition/signal processing controller" };
823 static char const * const pci_bridges[] =
824 { "Host/PCI Bridge", "ISA Bridge", "EISA Bridge", "Micro Channel Bridge",
825 "PCI Bridge", "PCMCIA Bridge", "NuBus Bridge", "CardBus Bridge" };
829 dump_res_rec(Resource_list const *r, int indent)
831 for (Resource_list::iterator i = r->begin(); i!= r->end(); ++i)
835 //dump_res_rec(i->child(), indent + 2);
840 Pci_dev::dump(int indent) const
842 char const *classname = "";
844 if (cls_rev >> 24 < sizeof(pci_classes)/sizeof(pci_classes[0]))
845 classname = pci_classes[cls_rev >> 24];
847 if ((cls_rev >> 24) == 0x06)
849 unsigned sc = (cls_rev >> 16) & 0xff;
850 if (sc < sizeof(pci_bridges)/sizeof(pci_bridges[0]))
851 classname = pci_bridges[sc];
854 printf("%*.s%04x:%02x:%02x.%x: %s [%d]\n", indent, " ",
855 0, (int)_bus->num, _host->adr() >> 16, _host->adr() & 0xffff,
856 classname, (int)hdr_type);
859 printf("%*.s 0x%04x 0x%04x\n", indent, " ", vendor(), device());
860 libpciids_name_device(buf, sizeof(buf), vendor(), device());
861 printf("%*.s %s\n", indent, " ", buf);
864 dump_res_rec(resources(), 0);
869 Pci_bridge::dump(int) const
872 "bridge %04x:%02x:%02x.%x\n",
873 b->num, 0, b->parent() ? (int)static_cast<Pci_bridge*>(b->parent())->num : 0,
874 b->adr() >> 16, b->adr() & 0xffff);
877 //dump_res_rec(resources(), 0);
879 for (iterator c = begin(0); c != end(); ++c)
887 Pci_cardbus_bridge::discover_resources(Hw::Device *host)
889 using namespace Hw::Pci;
891 cfg_read(C_subsys_vendor, &v, Cfg_long);
894 Adr_resource *r = new Adr_resource_provider(Resource::Mmio_res);
895 cfg_read(C_cb_mem_base_0, &v, Cfg_long);
897 cfg_read(C_cb_mem_limit_0, &v, Cfg_long);
902 host->add_resource(r);
904 r = new Adr_resource_provider(Resource::Mmio_res);
905 cfg_read(C_cb_mem_base_1, &v, Cfg_long);
907 cfg_read(C_cb_mem_limit_1, &v, Cfg_long);
912 host->add_resource(r);
914 r = new Adr_resource_provider(Resource::Io_res);
915 cfg_read(C_cb_io_base_0, &v, Cfg_long);
917 cfg_read(C_cb_io_limit_0, &v, Cfg_long);
922 host->add_resource(r);
924 r = new Adr_resource_provider(Resource::Io_res);
925 cfg_read(C_cb_io_base_1, &v, Cfg_long);
927 cfg_read(C_cb_io_limit_1, &v, Cfg_long);
932 host->add_resource(r);
936 Pci_irq_router::dump(int indent) const
938 printf("%*sPCI IRQ ROUTER: %s (%p)\n", indent, "", typeid(*this).name(),
943 Pci_pci_bridge_irq_router_rs::request(Resource *parent, Device *pdev,
944 Resource *child, Device *cdev)
946 Adr_resource *cr = dynamic_cast<Adr_resource*>(child);
950 // assume we allocate an abstract IRQ router resource as a child
952 child->parent(parent);
958 Hw::Device *cd = dynamic_cast<Hw::Device*>(cdev);
965 cr->start((cr->start() + (cd->adr() >> 16)) & 3);
966 res = pdev->parent()->request_child_resource(cr, pdev);