summaryrefslogtreecommitdiff
path: root/kernel/pci.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/pci.c')
-rw-r--r--kernel/pci.c61
1 files changed, 61 insertions, 0 deletions
diff --git a/kernel/pci.c b/kernel/pci.c
new file mode 100644
index 0000000..5cd2102
--- /dev/null
+++ b/kernel/pci.c
@@ -0,0 +1,61 @@
+//
+// simple PCI-Express initialization, only
+// works for qemu and its e1000 card.
+//
+
+#include "types.h"
+#include "param.h"
+#include "memlayout.h"
+#include "riscv.h"
+#include "spinlock.h"
+#include "proc.h"
+#include "defs.h"
+
+void
+pci_init()
+{
+ // we'll place the e1000 registers at this address.
+ // vm.c maps this range.
+ uint64 e1000_regs = 0x40000000L;
+
+ // qemu -machine virt puts PCIe config space here.
+ // vm.c maps this range.
+ uint32 *ecam = (uint32 *) 0x30000000L;
+
+ // look at each possible PCI device on bus 0.
+ for(int dev = 0; dev < 32; dev++){
+ int bus = 0;
+ int func = 0;
+ int offset = 0;
+ uint32 off = (bus << 16) | (dev << 11) | (func << 8) | (offset);
+ volatile uint32 *base = ecam + off;
+ uint32 id = base[0];
+
+ // 100e:8086 is an e1000
+ if(id == 0x100e8086){
+ // command and status register.
+ // bit 0 : I/O access enable
+ // bit 1 : memory access enable
+ // bit 2 : enable mastering
+ base[1] = 7;
+ __sync_synchronize();
+
+ for(int i = 0; i < 6; i++){
+ uint32 old = base[4+i];
+
+ // writing all 1's to the BAR causes it to be
+ // replaced with its size.
+ base[4+i] = 0xffffffff;
+ __sync_synchronize();
+
+ base[4+i] = old;
+ }
+
+ // tell the e1000 to reveal its registers at
+ // physical address 0x40000000.
+ base[4+0] = e1000_regs;
+
+ e1000_init((uint32*)e1000_regs);
+ }
+ }
+}