diff options
author | Ian Moffett <ian@osmora.org> | 2024-06-02 23:06:29 -0400 |
---|---|---|
committer | Ian Moffett <ian@osmora.org> | 2024-06-02 23:06:29 -0400 |
commit | 614ffa927eb7acb1c1fb31f5186f5e3d4b1bca74 (patch) | |
tree | 6801e3bad0fd1a69667ead466d65704029e1763b | |
parent | ea4f3cfceb3e3f926a517ab0bb2373c8f4152671 (diff) |
kernel: pci: Add PCI BAR mapping routine
Signed-off-by: Ian Moffett <ian@osmora.org>
-rw-r--r-- | sys/dev/pci/pci.c | 58 | ||||
-rw-r--r-- | sys/include/dev/pci/pci.h | 1 |
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_ */ |