aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin O'Connor <kevin@koconnor.net>2016-02-02 22:09:57 -0500
committerKevin O'Connor <kevin@koconnor.net>2016-02-02 23:16:40 -0500
commitf1b1d76159a8e7091e783bbc296b928226ccf153 (patch)
treeda9ad594b7e9c478966d00c138827fc982ce91ef
parent88e9bd7caee694498cadccba5c1e63baad6d83ab (diff)
downloadseabios-hppa-f1b1d76159a8e7091e783bbc296b928226ccf153.zip
seabios-hppa-f1b1d76159a8e7091e783bbc296b928226ccf153.tar.gz
seabios-hppa-f1b1d76159a8e7091e783bbc296b928226ccf153.tar.bz2
pci: Add helper functions for internal driver BAR handling
Add functions to verify and obtain PCI BARs (Base Address Registers). These new functions check that the requested BAR is of the right type and appears valid. Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
-rw-r--r--src/hw/pci.c58
-rw-r--r--src/hw/pci.h3
2 files changed, 61 insertions, 0 deletions
diff --git a/src/hw/pci.c b/src/hw/pci.c
index a241d06..86b7d54 100644
--- a/src/hw/pci.c
+++ b/src/hw/pci.c
@@ -10,6 +10,7 @@
#include "pci.h" // pci_config_writel
#include "pci_regs.h" // PCI_VENDOR_ID
#include "romfile.h" // romfile_loadint
+#include "stacks.h" // wait_preempt
#include "string.h" // memset
#include "util.h" // udelay
#include "x86.h" // outl
@@ -271,6 +272,63 @@ int pci_bridge_has_region(struct pci_device *pci,
return pci_config_readb(pci->bdf, base) != 0;
}
+// Enable PCI bus-mastering (ie, DMA) support on a pci device
+void
+pci_enable_busmaster(struct pci_device *pci)
+{
+ ASSERT32FLAT();
+ wait_preempt();
+ pci_config_maskw(pci->bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER);
+}
+
+// Verify an IO bar and return it to the caller
+u16
+pci_enable_iobar(struct pci_device *pci, u32 addr)
+{
+ ASSERT32FLAT();
+ wait_preempt();
+ u32 bar = pci_config_readl(pci->bdf, addr);
+ if (!(bar & PCI_BASE_ADDRESS_SPACE_IO)) {
+ warn_internalerror();
+ return 0;
+ }
+ bar &= PCI_BASE_ADDRESS_IO_MASK;
+ if (bar == 0 || bar > 0xffff) {
+ warn_internalerror();
+ return 0;
+ }
+ pci_config_maskw(pci->bdf, PCI_COMMAND, 0, PCI_COMMAND_IO);
+ return bar;
+}
+
+// Verify a memory bar and return it to the caller
+void *
+pci_enable_membar(struct pci_device *pci, u32 addr)
+{
+ ASSERT32FLAT();
+ wait_preempt();
+ u32 bar = pci_config_readl(pci->bdf, addr);
+ if (bar & PCI_BASE_ADDRESS_SPACE_IO) {
+ warn_internalerror();
+ return NULL;
+ }
+ if (bar & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+ u32 high = pci_config_readl(pci->bdf, addr+4);
+ if (high) {
+ dprintf(1, "Can not map memory bar over 4Gig\n");
+ return NULL;
+ }
+ }
+ bar &= PCI_BASE_ADDRESS_MEM_MASK;
+ if (bar + 4*1024*1024 < 20*1024*1024) {
+ // Bar doesn't look valid (it is in last 4M or first 16M)
+ warn_internalerror();
+ return NULL;
+ }
+ pci_config_maskw(pci->bdf, PCI_COMMAND, 0, PCI_COMMAND_MEMORY);
+ return (void*)bar;
+}
+
void
pci_reboot(void)
{
diff --git a/src/hw/pci.h b/src/hw/pci.h
index fc5e7b9..8e39753 100644
--- a/src/hw/pci.h
+++ b/src/hw/pci.h
@@ -126,6 +126,9 @@ struct pci_device *pci_find_init_device(const struct pci_device_id *ids
u8 pci_find_capability(struct pci_device *pci, u8 cap_id, u8 cap);
int pci_bridge_has_region(struct pci_device *pci,
enum pci_region_type region_type);
+void pci_enable_busmaster(struct pci_device *pci);
+u16 pci_enable_iobar(struct pci_device *pci, u32 addr);
+void *pci_enable_membar(struct pci_device *pci, u32 addr);
void pci_reboot(void);
#endif