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_port_root_bridge::cfg_read(Cfg_addr addr, l4_uint32_t *value, Cfg_width w)
64 l4util_out32((addr.to_compat_addr() | 0x80000000) & ~3UL, 0xcf8);
65 using namespace Hw::Pci;
69 case Cfg_byte: *value = l4util_in8(0xcfc + addr.reg_offs(w)); break;
70 case Cfg_short: *value = l4util_in16(0xcfc + addr.reg_offs(w)); break;
71 case Cfg_long: *value = l4util_in32(0xcfc); break;
77 Pci_port_root_bridge::cfg_write(Cfg_addr addr, l4_uint32_t value, Cfg_width w)
79 l4util_out32((addr.to_compat_addr() | 0x80000000) & ~3UL, 0xcf8);
80 using namespace Hw::Pci;
84 case Cfg_byte: l4util_out8(value, 0xcfc + addr.reg_offs(w)); break;
85 case Cfg_short: l4util_out16(value, 0xcfc + addr.reg_offs(w)); break;
86 case Cfg_long: l4util_out32(value, 0xcfc); break;
94 static inline l4_uint32_t
95 devfn(unsigned dev, unsigned fn)
96 { return (dev << 16) | fn; }
101 Pci_bridge::scan_bus()
103 using namespace Hw::Pci;
106 for (device = 0; device < 32; ++device)
108 l4_uint32_t hdr_type;
109 cfg_read(num, devfn(device, 0), Pci_dev::C_header_type, &hdr_type, Cfg_byte);
110 int funcs = (hdr_type & 0x80) ? 8 : 1;
111 for (int function = 0; function < funcs; ++function)
113 l4_uint32_t vendor, _class;
114 cfg_read(num, devfn(device, function), Pci_dev::C_vendor, &vendor, Cfg_short);
115 if (vendor == 0xffff)
123 cfg_read(num, devfn(device, function), Pci_dev::C_vendor, &vendor, Cfg_long);
124 cfg_read(num, devfn(device, function), Pci_dev::C_class_rev, &_class, Cfg_long);
127 cfg_read(num, devfn(device, function), Pci_dev::C_header_type, &hdr_type, Cfg_byte);
129 Hw::Device *child = host()->get_child_dev_adr(devfn(device, function), true);
132 if ((_class >> 16) == 0x0604 || (_class >> 16) == 0x0607)
134 Pci_pci_bridge_basic *b = 0;
135 if ((hdr_type & 0x7f) == 1)
136 b = new Pci_pci_bridge(child, this);
137 else if((hdr_type & 0x7f) == 2)
138 b = new Pci_cardbus_bridge(child, this);
140 child->set_discover_bus_if(b);
143 bool reassign_busses = false;
145 cfg_read(num, devfn(device, function), Pci_dev::C_primary, &busses, Cfg_long);
147 if ((busses & 0xff) != num
148 || ((busses >> 8) & 0xff) <= num)
149 reassign_busses = true;
153 unsigned new_so = subordinate + 1;
156 b->subordinate = b->num;
158 busses = (busses & 0xff000000)
159 | ((l4_uint32_t)(b->pri))
160 | ((l4_uint32_t)(b->num) << 8)
161 | ((l4_uint32_t)(b->subordinate) << 16);
163 cfg_write(num, devfn(device, function), Pci_dev::C_primary, busses, Cfg_long);
164 increase_subordinate(new_so);
168 b->pri = busses & 0xff;
169 b->num = (busses >> 8) & 0xff;
170 b->subordinate = (busses >> 16) & 0xff;
176 d = new Pci_dev(child, this);
178 child->add_feature(d);
179 child->add_resource_discoverer(d);
181 d->vendor_device = vendor;
183 d->hdr_type = hdr_type;
195 Base_lo = 0xfee00000,
200 Dest_mode_phys = 0 << 2,
201 Dest_mode_log = 1 << 2,
206 Dest_redir_cpu = 0 << 3,
207 Dest_redir_lowprio = 1 << 3,
212 Data_del_fixed = 0 << 8,
213 Data_del_lowprio = 1 << 8,
218 Data_level_deassert = 0 << 14,
219 Data_level_assert = 1 << 14,
224 Data_trigger_edge = 0 << 15,
225 Data_trigger_level = 1 << 15,
228 static unsigned data(int vector) { return vector & 0xff; }
232 Pci_dev::cfg_word(unsigned w) const
237 case 0: return &vendor_device;
238 case 2: return &cls_rev;
239 case 11: return &subsys_ids;
244 Pci_dev::bus_nr() const
245 { return _bus->num; }
248 Pci_dev::disable_decoders()
252 cfg_read(C_command, &v, Hw::Pci::Cfg_byte);
253 cfg_write(C_command, v & ~3, Hw::Pci::Cfg_byte);
258 Pci_dev::restore_decoders(unsigned cmd)
260 cfg_write(C_command, cmd, Hw::Pci::Cfg_byte);
264 Pci_dev::discover_bar(int bar)
266 using namespace Hw::Pci;
270 int r = C_bar_0 + bar * 4;
271 l4_uint8_t cmd = disable_decoders();
273 cfg_read(r, &v, Cfg_long);
274 cfg_write(r, ~0U, Cfg_long);
275 cfg_read(r, &x, Cfg_long);
276 cfg_write(r, v, Cfg_long);
278 restore_decoders(cmd);
283 unsigned io_flags = (cmd & CC_io) ? 0 : Resource::F_disabled;
284 unsigned mem_flags = (cmd & CC_mem) ? 0 : Resource::F_disabled;
286 io_flags |= Resource::Io_res
287 | Resource::F_size_aligned
288 | Resource::F_hierarchical;
290 mem_flags |= Resource::Mmio_res
291 | Resource::F_size_aligned
292 | Resource::F_hierarchical;
297 //printf("%08x: BAR[%d] mmio ... %x\n", adr(), bar, x );
298 res = new Resource(mem_flags);
299 if ((x & 0x6) == 0x4)
300 res->add_flags(Resource::F_width_64bit);
303 res->add_flags(Resource::F_prefetchable);
305 l4_uint64_t size = x & ~0x7f;
306 l4_uint64_t a = v & ~0x7f;
311 cmd = disable_decoders();
312 cfg_read(r, &v, Cfg_long);
313 cfg_write(r, ~0U, Cfg_long);
314 cfg_read(r, &x, Cfg_long);
315 cfg_write(r, v, Cfg_long);
316 restore_decoders(cmd);
317 a |= l4_uint64_t(v) << 32;
318 size |= l4_uint64_t(x) << 32;
321 for (int s = 7; s < 64; ++s)
328 res->start_size(a, size);
330 // printf("%08x: BAR[%d] mem ...\n", adr(), bar*4 + 10 );
331 _bars[bar - res->is_64bit()] = res;
333 _bars[bar] = (Resource*)1;
338 // printf("%08x: BAR[%d] io ...\n", adr(), bar );
340 for (s = 2; s < 32; ++s)
344 res = new Resource(io_flags);
347 res->start_size(v & ~3, 1 << s);
351 _host->add_resource(res);
356 Pci_dev::discover_legacy_ide_resources()
359 if (!Io_config::cfg->legacy_ide_resources(_host))
362 unsigned io_flags = Resource::Io_res
363 | Resource::F_size_aligned
364 | Resource::F_hierarchical;
366 // IDE legacy IO interface
367 if (cls_rev >> 16 == 0x101 && !(cls_rev & 0x100))
369 _host->add_resource(new Resource(io_flags, 0x1f0, 0x1f7));
370 _host->add_resource(new Resource(io_flags, 0x3f6, 0x3f6));
371 _host->add_resource(new Resource(Resource::Irq_res | Resource::Irq_type_raising_edge, 14, 14));
373 if (cls_rev >> 16 == 0x101 && !(cls_rev & 0x400))
375 _host->add_resource(new Resource(io_flags, 0x170, 0x177));
376 _host->add_resource(new Resource(io_flags, 0x376, 0x376));
377 _host->add_resource(new Resource(Resource::Irq_res | Resource::Irq_type_raising_edge, 15, 15));
382 Pci_dev::quirk_8086_8108()
384 using namespace Hw::Pci;
386 if (!(vendor() == 0x8086 && device() == 0x8108))
391 // GC - Graphics Control
392 cfg_read(0x52, &v, Cfg_short);
394 unsigned gfx_mem_sz = 0;
400 switch ((v >> 4) & 7)
402 case 1: gfx_mem_sz = 1 << 20; break;
403 case 2: gfx_mem_sz = 4 << 20; break;
404 case 3: gfx_mem_sz = 8 << 20; break;
408 // BSM - Base of Stolen Memory
409 cfg_read(0x5c, &v, Cfg_long);
412 unsigned flags = Resource::Mmio_res
413 | Resource::F_prefetchable
414 | Resource::F_fixed_addr
415 | Resource::F_fixed_size;
416 l4_addr_t end = v + gfx_mem_sz - 1;
418 _host->add_resource(new Resource(flags, v, end));
419 for (Pci_bridge *p = _bus; p;)
421 p->host()->add_resource(new Resource_provider(flags, v, end));
422 if (Pci_dev *d = dynamic_cast<Pci_dev *>(p))
430 Pci_dev::discover_expansion_rom()
432 using namespace Hw::Pci;
434 if (!Io_config::cfg->expansion_rom(host()))
438 unsigned rom_register = ((hdr_type & 0x7f) == 0) ? 12 * 4 : 14 * 4;
440 cfg_read(rom_register, &v, Cfg_long);
442 if (v == 0xffffffff || v == 0)
443 return; // no expansion ROM
445 cfg_write(rom_register, 0xfffffffe, Cfg_long);
446 cfg_read(rom_register, &x, Cfg_long);
447 cfg_write(rom_register, v, Cfg_long);
452 //printf("ROM %08x: %08x %08x\n", adr(), x, v);
455 for (s = 2; s < 32; ++s)
459 if (0) // currently we do not add a resource record for ROMs
461 unsigned flags = Resource::Mmio_res | Resource::F_size_aligned
462 | Resource::F_rom | Resource::F_prefetchable;
463 Resource *res = new Resource(flags);
466 res->start_size(v & ~3, 1 << s);
468 _host->add_resource(res);
474 class Msi_res : public Hw::Msi_resource
477 Msi_res(unsigned msi, Pci_bridge *bus, Hw::Pci::Cfg_addr cfg_addr)
478 : Msi_resource(msi), _bus(bus), _cfg_addr(cfg_addr)
481 int bind(L4::Cap<L4::Irq> irq, unsigned mode);
484 void dump(int indent) const
485 { Resource::dump("MSI ", indent); }
489 Hw::Pci::Cfg_addr _cfg_addr;
493 Msi_res::bind(L4::Cap<L4::Irq> irq, unsigned mode)
495 using namespace Hw::Pci;
497 int err = Msi_resource::bind(irq, mode);
502 int e2 = l4_error(system_icu()->icu->msi_info(pin(), &msg));
505 d_printf(DBG_ERR, "ERROR: could not get MSI message (pin=%x)\n", pin());
513 _bus->cfg_read(_cfg_addr + 2, &ctl, Cfg_short);
517 _bus->cfg_write(_cfg_addr + 4, Msi::Base_lo | Msi::Dest_mode_phys | Msi::Dest_redir_cpu, Cfg_long);
520 _bus->cfg_write(_cfg_addr + 8, Msi::Base_hi, Cfg_long);
522 _bus->cfg_write(_cfg_addr + msg_offs, Msi::Data_level_assert | Msi::Data_trigger_edge | Msi::Data_del_fixed | Msi::data(msg), Cfg_short);
524 _bus->cfg_write(_cfg_addr + 2, ctl | 1, Cfg_short);
526 d_printf(DBG_DEBUG2, "MSI: enable kernel PIN=%x hwpci=%02x:%02x.%x: reg=%03x msg=%lx\n",
527 pin(), _cfg_addr.bus(), _cfg_addr.dev(), _cfg_addr.fn(),
528 _cfg_addr.reg(), msg);
536 using namespace Hw::Pci;
539 _bus->cfg_read(_cfg_addr + 2, &ctl, Cfg_short);
540 _bus->cfg_write(_cfg_addr + 2, ctl & ~1, Cfg_short);
542 return Msi_resource::unbind();
548 Pci_dev::discover_pci_caps()
550 using namespace Hw::Pci;
554 cfg_read(C_status, &status, Cfg_short);
555 if (!(status & CS_cap_list))
560 cfg_read(C_capability_ptr, &cap_ptr, Cfg_byte);
562 for (; cap_ptr; cfg_read(cap_ptr + 1, &cap_ptr, Cfg_byte), cap_ptr &= ~0x3)
565 cfg_read(cap_ptr, &id, Cfg_byte);
566 // printf(" PCI-cap: %x %s %s\n", id, Io_config::cfg->transparent_msi(host()) ? "yes" : "no", system_icu()->info.supports_msi() ? "yes" : "no" );
567 if (id == 0x05 && Io_config::cfg->transparent_msi(host())
568 && system_icu()->info.supports_msi())
572 unsigned msi = _last_msi++;
573 if (msi >= system_icu()->info.nr_msis)
575 d_printf(DBG_WARN, "WARNING: run out of MSI vectors, use normal IRQ\n");
579 for (Resource_list::const_iterator i = host()->resources()->begin();
580 i != host()->resources()->end(); ++i)
582 if ((*i)->type() == Resource::Irq_res)
585 (*i)->add_flags(Resource::F_disabled);
589 d_printf(DBG_DEBUG, "Use MSI PCI device %02x:%02x:%x: pin=%x\n",
590 bus()->num, host()->adr() >> 16, host()->adr() & 0xff, msi);
592 Resource *res = new Msi_res(msi, bus(), cfg_addr(cap_ptr));
594 _host->add_resource(res);
600 Pci_dev::discover_resources(Hw::Device *host)
602 using namespace Hw::Pci;
604 // printf("survey ... %x.%x\n", dynamic_cast<Pci_bridge*>(parent())->num, adr());
606 cfg_read(C_subsys_vendor, &v, Cfg_long);
608 cfg_read(C_irq_pin, &v, Cfg_byte);
613 Resource * r = new Resource(Resource::Irq_res | Resource::F_relative
614 | Resource::F_hierarchical,
615 irq_pin - 1, irq_pin - 1);
617 host->add_resource(r); //new Resource(Resource::Irq_res | Resource::F_relative
618 // | Resource::F_hierarchical,
619 // irq_pin - 1, irq_pin - 1));
622 cfg_read(C_command, &v, Cfg_short);
624 int bars = ((hdr_type & 0x7f) == 0) ? 6 : 2;
626 discover_legacy_ide_resources();
630 for (int bar = 0; bar < bars;)
631 bar = discover_bar(bar);
633 discover_expansion_rom();
638 Pci_dev::setup_resources(Hw::Device *)
640 using namespace Hw::Pci;
642 for (unsigned i = 0; i < sizeof(_bars)/sizeof(_bars[0]); ++i)
644 Resource *r = bar(i);
645 if (!r || r->type() == Resource::Io_res)
651 int reg = 0x10 + i * 4;
652 l4_uint64_t s = r->start();
653 l4_uint8_t cmd = disable_decoders();
654 cfg_write(reg, s, Cfg_long);
657 cfg_write(reg + 4, s >> 32, Cfg_long);
660 restore_decoders(cmd);
664 cfg_read(reg, &v, Cfg_long);
665 if (l4_uint32_t(v & ~0x7f) != l4_uint32_t(s & 0xffffffff))
666 d_printf(DBG_ERR, "ERROR: could not set PCI BAR %d\n", i);
668 // printf("%08x: set BAR[%d] to %08x\n", adr(), i, v);
674 Pci_dev::match_cid(cxx::String const &_cid) const
676 cxx::String const prefix("PCI/");
677 cxx::String cid(_cid);
678 if (!cid.starts_with(prefix))
681 cid = cid.substr(prefix.len());
682 cxx::String::Index n;
683 for (; cid.len() > 0; cid = cid.substr(n + 1))
686 cxx::String tok = cid.head(n);
690 if (tok.starts_with("CC_"))
697 int l = tok.from_hex(&_csr);
698 if (l < 0 || l > 6 || l % 2)
701 if ((cls_rev >> (8 + (6-l) * 4)) == _csr)
706 else if (tok.starts_with("REV_"))
710 if (tok.len() != 2 || tok.from_hex(&r) != 2)
713 if (r != (cls_rev & 0xff))
716 else if (tok.starts_with("VEN_"))
720 if (tok.len() != 4 || tok.from_hex(&v) != 4)
723 if ((vendor_device & 0xffff) != v)
726 else if (tok.starts_with("DEV_"))
730 if (tok.len() != 4 || tok.from_hex(&d) != 4)
733 if (((vendor_device >> 16) & 0xffff) != d)
736 else if (tok.starts_with("SUBSYS_"))
740 if (tok.len() != 8 || tok.from_hex(&s) != 8)
753 Pci_pci_bridge::setup_resources(Hw::Device *host)
755 using namespace Hw::Pci;
757 Pci_dev::setup_resources(host);
759 if (!mmio->empty() && mmio->valid())
761 l4_uint32_t v = (mmio->start() >> 16) & 0xfff0;
762 v |= mmio->end() & 0xfff00000;
763 cfg_write(0x20, v, Cfg_long);
764 // printf("%08x: set mmio to %08x\n", adr(), v);
766 cfg_read(0x20, &r, Cfg_long);
767 // printf("%08x: mmio = %08x\n", adr(), r);
768 cfg_read(0x04, &r, Cfg_short);
770 cfg_write(0x4, r, Cfg_short);
773 if (!pref_mmio->empty() && pref_mmio->valid())
775 l4_uint32_t v = (pref_mmio->start() >> 16) & 0xfff0;
776 v |= pref_mmio->end() & 0xfff00000;
777 cfg_write(0x24, v, Cfg_long);
778 // printf("%08x: set pref mmio to %08x\n", adr(), v);
783 Pci_pci_bridge::discover_resources(Hw::Device *host)
785 using namespace Hw::Pci;
790 cfg_read(C_mem_base, &v, Cfg_long);
791 s = (v & 0xfff0) << 16;
792 e = (v & 0xfff00000) | 0xfffff;
794 Resource *r = new Resource_provider(Resource::Mmio_res);
795 r->alignment(0xfffff);
801 // printf("%08x: mmio = %08x\n", adr(), v);
804 _host->add_resource(mmio);
806 r = new Resource_provider(Resource::Mmio_res | Resource::F_prefetchable);
807 cfg_read(C_pref_mem_base, &v, Cfg_long);
808 s = (v & 0xfff0) << 16;
809 e = (v & 0xfff00000) | 0xfffff;
813 r->add_flags(Resource::F_width_64bit);
814 cfg_read(C_pref_mem_base_hi, &v, Cfg_long);
815 s |= l4_uint64_t(v) << 32;
816 cfg_read(C_pref_mem_limit_hi, &v, Cfg_long);
817 e |= l4_uint64_t(v) << 32;
820 r->alignment(0xfffff);
828 _host->add_resource(r);
830 cfg_read(C_io_base, &v, Cfg_short);
832 e = (v & 0xf000) | 0xfff;
834 r = new Resource_provider(Resource::Io_res);
842 _host->add_resource(r);
844 Pci_dev::discover_resources(host);
849 Pci_root_bridge::discover_resources(Hw::Device *)
855 static const char * const pci_classes[] =
856 { "unknown", "mass storage contoller", "network controller",
857 "display controller", "multimedia device", "memory controller",
858 "bridge device", "simple communication controller",
859 "system peripheral", "input device", "docking station",
860 "processor", "serial bus controller", "wireless controller",
861 "intelligent I/O controller", "satellite communication controller",
862 "encryption/decryption controller",
863 "data aquisition/signal processing controller" };
865 static char const * const pci_bridges[] =
866 { "Host/PCI Bridge", "ISA Bridge", "EISA Bridge", "Micro Channel Bridge",
867 "PCI Bridge", "PCMCIA Bridge", "NuBus Bridge", "CardBus Bridge" };
872 dump_res_rec(Resource_list const *r, int indent)
874 for (Resource_list::iterator i = r->begin(); i!= r->end(); ++i)
878 //dump_res_rec(i->child(), indent + 2);
884 Pci_dev::dump(int indent) const
886 char const *classname = "";
888 if (cls_rev >> 24 < sizeof(pci_classes)/sizeof(pci_classes[0]))
889 classname = pci_classes[cls_rev >> 24];
891 if ((cls_rev >> 24) == 0x06)
893 unsigned sc = (cls_rev >> 16) & 0xff;
894 if (sc < sizeof(pci_bridges)/sizeof(pci_bridges[0]))
895 classname = pci_bridges[sc];
898 printf("%*.s%04x:%02x:%02x.%x: %s [%d]\n", indent, " ",
899 0, (int)_bus->num, _host->adr() >> 16, _host->adr() & 0xffff,
900 classname, (int)hdr_type);
903 printf("%*.s 0x%04x 0x%04x\n", indent, " ", vendor(), device());
904 libpciids_name_device(buf, sizeof(buf), vendor(), device());
905 printf("%*.s %s\n", indent, " ", buf);
908 dump_res_rec(resources(), 0);
913 Pci_bridge::dump(int) const
916 "bridge %04x:%02x:%02x.%x\n",
917 b->num, 0, b->parent() ? (int)static_cast<Pci_bridge*>(b->parent())->num : 0,
918 b->adr() >> 16, b->adr() & 0xffff);
921 //dump_res_rec(resources(), 0);
923 for (iterator c = begin(0); c != end(); ++c)
931 Pci_cardbus_bridge::discover_resources(Hw::Device *host)
933 using namespace Hw::Pci;
935 cfg_read(C_subsys_vendor, &v, Cfg_long);
938 Resource *r = new Resource_provider(Resource::Mmio_res);
939 cfg_read(C_cb_mem_base_0, &v, Cfg_long);
941 cfg_read(C_cb_mem_limit_0, &v, Cfg_long);
946 host->add_resource(r);
948 r = new Resource_provider(Resource::Mmio_res);
949 cfg_read(C_cb_mem_base_1, &v, Cfg_long);
951 cfg_read(C_cb_mem_limit_1, &v, Cfg_long);
956 host->add_resource(r);
958 r = new Resource_provider(Resource::Io_res);
959 cfg_read(C_cb_io_base_0, &v, Cfg_long);
961 cfg_read(C_cb_io_limit_0, &v, Cfg_long);
966 host->add_resource(r);
968 r = new Resource_provider(Resource::Io_res);
969 cfg_read(C_cb_io_base_1, &v, Cfg_long);
971 cfg_read(C_cb_io_limit_1, &v, Cfg_long);
976 host->add_resource(r);
980 Pci_irq_router::dump(int indent) const
982 printf("%*sPCI IRQ ROUTER: %s (%p)\n", indent, "", typeid(*this).name(),
987 Pci_pci_bridge_irq_router_rs::request(Resource *parent, Device *pdev,
988 Resource *child, Device *cdev)
992 Hw::Device *cd = dynamic_cast<Hw::Device*>(cdev);
999 child->start((child->start() + (cd->adr() >> 16)) & 3);
1000 res = pdev->parent()->request_child_resource(child, pdev);
1002 child->parent(parent);