summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Moffett <ian@osmora.org>2024-06-02 23:06:29 -0400
committerIan Moffett <ian@osmora.org>2024-06-02 23:06:29 -0400
commit614ffa927eb7acb1c1fb31f5186f5e3d4b1bca74 (patch)
tree6801e3bad0fd1a69667ead466d65704029e1763b
parentea4f3cfceb3e3f926a517ab0bb2373c8f4152671 (diff)
kernel: pci: Add PCI BAR mapping routine
Signed-off-by: Ian Moffett <ian@osmora.org>
-rw-r--r--sys/dev/pci/pci.c58
-rw-r--r--sys/include/dev/pci/pci.h1
2 files changed, 59 insertions, 0 deletions
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c
index 006b428..3be61d9 100644
--- a/sys/dev/pci/pci.c
+++ b/sys/dev/pci/pci.c
@@ -33,7 +33,9 @@
#include <sys/panic.h>
#include <sys/queue.h>
#include <sys/syslog.h>
+#include <sys/errno.h>
#include <vm/dynalloc.h>
+#include <machine/bus.h>
#if defined(__x86_64__)
#include <machine/io.h>
#endif
@@ -183,6 +185,62 @@ pci_scan_bus(uint8_t bus)
}
/*
+ * Convert a BAR number to BAR register offset.
+ *
+ * @dev: Device of BAR to check.
+ * @bar: Bar number.
+ */
+static uint8_t
+pci_get_barreg(struct pci_device *dev, uint8_t bar)
+{
+ switch (bar) {
+ case 0: return PCIREG_BAR0;
+ case 1: return PCIREG_BAR1;
+ case 2: return PCIREG_BAR2;
+ case 3: return PCIREG_BAR3;
+ case 4: return PCIREG_BAR4;
+ case 5: return PCIREG_BAR5;
+ default: return 0;
+ }
+}
+
+/*
+ * Map a PCI(e) BAR into kernel memory.
+ *
+ * @dev: Device of BAR to map.
+ * @bar: BAR number to map.
+ * @vap: Resulting virtual address.
+ */
+int
+pci_map_bar(struct pci_device *dev, uint8_t bar, void **vap)
+{
+ uint8_t bar_reg = pci_get_barreg(dev, bar);
+ uintptr_t tmp;
+ uint32_t size;
+
+ if (bar_reg == 0) {
+ return -EINVAL;
+ }
+
+ /*
+ * Get the length of the region this BAR covers by writing a
+ * mask of 32 bits into the BAR register and seeing how many
+ * bits are unset. We can use this to compute the size of the
+ * region. We know that log2(len) bits must be unset.
+ */
+ tmp = pci_readl(dev, bar_reg);
+ pci_writel(dev, bar_reg, __MASK(32));
+ size = pci_readl(dev, bar_reg);
+ size = ~size + 1;
+
+ /* Now we need to restore the previous value */
+ pci_writel(dev, bar_reg, tmp);
+
+ /* Now do the actual mapping work */
+ return bus_map(dev->bar[bar], size, 0, vap);
+}
+
+/*
* Read PCI(e) configuration space.
*
* @dev: Device to read from.
diff --git a/sys/include/dev/pci/pci.h b/sys/include/dev/pci/pci.h
index ad3f7ac..dbbd7ce 100644
--- a/sys/include/dev/pci/pci.h
+++ b/sys/include/dev/pci/pci.h
@@ -77,6 +77,7 @@ int pci_init(void);
uint32_t pci_readl(struct pci_device *dev, uint32_t offset);
void pci_writel(struct pci_device *dev, uint32_t offset, uint32_t val);
void pci_set_cmdreg(struct pci_device *dev, uint16_t bits);
+int pci_map_bar(struct pci_device *dev, uint8_t bar, void **vap);
struct pci_device *pci_get_device(struct pci_lookup lookup, uint16_t lookup_type);
#endif /* !_DEV_PCI_H_ */