summaryrefslogtreecommitdiff
path: root/sys/dev/pci
diff options
context:
space:
mode:
authorMark Kettenis <kettenis@cvs.openbsd.org>2010-04-21 18:55:41 +0000
committerMark Kettenis <kettenis@cvs.openbsd.org>2010-04-21 18:55:41 +0000
commit403a35f383969fa15140ed4da996a8be56ac6470 (patch)
tree2688d8ae89cb3fcade3ef4e52dbe285b31691669 /sys/dev/pci
parent039508336e38511115bb4db1b0f40a6ba98f4d6e (diff)
First stab at a VGA arbiter. The VGA arbiter makes sure that only a single
VGA device can be active, and is responsible for routing IO to the active VGA device. Processes can use the new PCIOC_GETVGA and PCIOC_SETVGA ioctls to manipulate the VGA arbiter. ok deraadt@, oga@
Diffstat (limited to 'sys/dev/pci')
-rw-r--r--sys/dev/pci/pci.c178
1 files changed, 176 insertions, 2 deletions
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c
index 8a95050bf71..c0abc728ba3 100644
--- a/sys/dev/pci/pci.c
+++ b/sys/dev/pci/pci.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pci.c,v 1.73 2010/01/26 17:47:36 otto Exp $ */
+/* $OpenBSD: pci.c,v 1.74 2010/04/21 18:55:40 kettenis Exp $ */
/* $NetBSD: pci.c,v 1.31 1997/06/06 23:48:04 thorpej Exp $ */
/*
@@ -39,6 +39,7 @@
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/malloc.h>
+#include <sys/proc.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
@@ -77,6 +78,11 @@ struct cfdriver pci_cd = {
int pci_ndomains;
+struct proc *pci_vga_proc;
+struct pci_softc *pci_vga_pci;
+pcitag_t pci_vga_tag;
+int pci_vga_count;
+
int pciprint(void *, const char *);
int pcisubmatch(struct device *, void *, void *);
@@ -87,6 +93,8 @@ int pci_enumerate_bus(struct pci_softc *,
int (*)(struct pci_attach_args *), struct pci_attach_args *);
#endif
int pci_reserve_resources(struct pci_attach_args *);
+int pci_count_vga(struct pci_attach_args *);
+int pci_primary_vga(struct pci_attach_args *);
/*
* Important note about PCI-ISA bridges:
@@ -167,6 +175,9 @@ pciattach(struct device *parent, struct device *self, void *aux)
sc->sc_intrswiz = pba->pba_intrswiz;
sc->sc_intrtag = pba->pba_intrtag;
pci_enumerate_bus(sc, pci_reserve_resources, NULL);
+ pci_enumerate_bus(sc, pci_count_vga, NULL);
+ if (pci_enumerate_bus(sc, pci_primary_vga, NULL))
+ pci_vga_pci = sc;
pci_enumerate_bus(sc, NULL, NULL);
}
@@ -768,6 +779,10 @@ pci_matchbyid(struct pci_attach_args *pa, const struct pci_matchid *ids,
#define PCIDEBUG(x)
#endif
+void pci_disable_vga(pci_chipset_tag_t, pcitag_t);
+void pci_enable_vga(pci_chipset_tag_t, pcitag_t);
+void pci_route_vga(struct pci_softc *);
+void pci_unroute_vga(struct pci_softc *);
int pciopen(dev_t dev, int oflags, int devtype, struct proc *p);
int pciclose(dev_t dev, int flag, int devtype, struct proc *p);
@@ -798,6 +813,8 @@ int
pciclose(dev_t dev, int flag, int devtype, struct proc *p)
{
PCIDEBUG(("pciclose\n"));
+
+ pci_vga_proc = NULL;
return (0);
}
@@ -809,7 +826,7 @@ pciioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
struct pci_rom *rom;
int i, error;
pcitag_t tag;
- struct pci_softc *pci = NULL;
+ struct pci_softc *pci;
pci_chipset_tag_t pc;
switch (cmd) {
@@ -822,6 +839,11 @@ pciioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
case PCIOCGETROMLEN:
case PCIOCGETROM:
break;
+ case PCIOCGETVGA:
+ case PCIOCSETVGA:
+ if (pci_vga_pci == NULL)
+ return EINVAL;
+ break;
default:
return ENOTTY;
}
@@ -950,6 +972,65 @@ pciioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
break;
}
+ case PCIOCGETVGA:
+ {
+ struct pci_vga *vga = (struct pci_vga *)data;
+ int bus, device, function;
+
+ pci_decompose_tag(pci_vga_pci->sc_pc, pci_vga_tag,
+ &bus, &device, &function);
+ vga->pv_sel.pc_bus = bus;
+ vga->pv_sel.pc_dev = device;
+ vga->pv_sel.pc_func = function;
+ error = 0;
+ break;
+ }
+ case PCIOCSETVGA:
+ {
+ struct pci_vga *vga = (struct pci_vga *)data;
+
+ switch (vga->pv_lock) {
+ case PCI_VGA_UNLOCK:
+ case PCI_VGA_LOCK:
+ case PCI_VGA_TRYLOCK:
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ if (vga->pv_lock == PCI_VGA_UNLOCK) {
+ if (pci_vga_proc != p)
+ return (EINVAL);
+ pci_vga_proc = NULL;
+ wakeup(&pci_vga_proc);
+ return (0);
+ }
+
+ while (pci_vga_proc != p && pci_vga_proc != NULL) {
+ if (vga->pv_lock == PCI_VGA_TRYLOCK)
+ return (EBUSY);
+ error = tsleep(&pci_vga_proc, PLOCK | PCATCH,
+ "vgalk", 0);
+ if (error)
+ return (error);
+ }
+ pci_vga_proc = p;
+
+ if (tag != pci_vga_tag) {
+ pci_disable_vga(pci_vga_pci->sc_pc, pci_vga_tag);
+ if (pci != pci_vga_pci) {
+ pci_unroute_vga(pci_vga_pci);
+ pci_route_vga(pci);
+ pci_vga_pci = pci;
+ }
+ pci_enable_vga(pc, tag);
+ pci_vga_tag = tag;
+ }
+
+ error = 0;
+ break;
+ }
+
default:
error = ENOTTY;
break;
@@ -959,3 +1040,96 @@ pciioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
}
#endif
+
+void
+pci_disable_vga(pci_chipset_tag_t pc, pcitag_t tag)
+{
+ pcireg_t csr;
+
+ csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
+ csr &= ~(PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE);
+ pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, csr);
+}
+
+void
+pci_enable_vga(pci_chipset_tag_t pc, pcitag_t tag)
+{
+ pcireg_t csr;
+
+ csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
+ csr |= PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE;
+ pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, csr);
+}
+
+void
+pci_route_vga(struct pci_softc *sc)
+{
+ pci_chipset_tag_t pc;
+ pcireg_t bc;
+
+ if (sc->sc_bridgetag == NULL)
+ return;
+
+ bc = pci_conf_read(pc, *sc->sc_bridgetag, PPB_REG_BRIDGECONTROL);
+ bc |= PPB_BC_VGA_ENABLE;
+ pci_conf_write(pc, *sc->sc_bridgetag, PPB_REG_BRIDGECONTROL, bc);
+
+ pci_route_vga((struct pci_softc *)sc->sc_dev.dv_parent->dv_parent);
+}
+
+void
+pci_unroute_vga(struct pci_softc *sc)
+{
+ pci_chipset_tag_t pc;
+ pcireg_t bc;
+
+ if (sc->sc_bridgetag == NULL)
+ return;
+
+ bc = pci_conf_read(pc, *sc->sc_bridgetag, PPB_REG_BRIDGECONTROL);
+ bc &= ~PPB_BC_VGA_ENABLE;
+ pci_conf_write(pc, *sc->sc_bridgetag, PPB_REG_BRIDGECONTROL, bc);
+
+ pci_unroute_vga((struct pci_softc *)sc->sc_dev.dv_parent->dv_parent);
+}
+
+int
+pci_count_vga(struct pci_attach_args *pa)
+{
+ /* XXX For now, only handle the first PCI domain. */
+ if (pa->pa_domain != 0)
+ return (0);
+
+ if ((PCI_CLASS(pa->pa_class) != PCI_CLASS_DISPLAY ||
+ PCI_SUBCLASS(pa->pa_class) != PCI_SUBCLASS_DISPLAY_VGA) &&
+ (PCI_CLASS(pa->pa_class) != PCI_CLASS_PREHISTORIC ||
+ PCI_SUBCLASS(pa->pa_class) != PCI_SUBCLASS_PREHISTORIC_VGA))
+ return (0);
+
+ pci_vga_count++;
+
+ return (0);
+}
+
+int
+pci_primary_vga(struct pci_attach_args *pa)
+{
+ /* XXX For now, only handle the first PCI domain. */
+ if (pa->pa_domain != 0)
+ return (0);
+
+ if ((PCI_CLASS(pa->pa_class) != PCI_CLASS_DISPLAY ||
+ PCI_SUBCLASS(pa->pa_class) != PCI_SUBCLASS_DISPLAY_VGA) &&
+ (PCI_CLASS(pa->pa_class) != PCI_CLASS_PREHISTORIC ||
+ PCI_SUBCLASS(pa->pa_class) != PCI_SUBCLASS_PREHISTORIC_VGA))
+ return (0);
+
+ if ((pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG)
+ & (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE))
+ != (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE))
+ return (0);
+
+ pci_vga_tag = pa->pa_tag;
+
+ return (1);
+}